20191024のPHPに関する記事は14件です。

【初歩】コールバック関数について調べた

よく聞くコールバック関数とは何か。どの様に使うのか。
自身の学習用メモとしてまとめます。
ご指摘あればお願いいたします。

コールバック関数とは

関数には引数を渡すことができますが、コールバック関数はその引数に、「別で作った関数」を渡すことができます。

まず引数とは、例えば下記の様に関数にあらかじめ仮引数を設定しておき、引数として値を渡せば関数で処理された結果が帰ってきます。

<?php
function getSum($num1, $num2){
  return $num1 + $num2;
}

$sum = getSum(2, 4);
echo $sum; // 6
?>

この引数に「関数」の処理を丸ごと渡してしまうのがコールバック関数。

用途

関数が実行された後に続けて、引数で渡した関数を実行することにより、処理のフローを制御できる。Aを実行したら続けてBを実行するということができる。非同期処理の際などによく使うらしい。


"I say"に続けて何か言葉を続けさせる処理を書いてみます。

PHPファイル

<?php

//コールバック関数を作る
function callback_func(){
  return "hello";
}

//コールバック関数を実行する関数を作る
function func($callback){
  echo "I say :".$callback();
}

コールバック関数の実行
func("callback_func"); //I say hello

?>

1、まず、コールバック関数を作ります。

今回コールバック関数としてcallback_funcと名付けた関数の処理内容は、helloと表示することとします。

function callback_func(){
  return "hello";
}

2、コールバック関数を実行する関数を作ります。

今回用意したfuncという関数の処理内容として、I sayと表示させます。
また、仮引数として$callbackを用意します。
これで以下の様に書けば、まずfuncの処理つまり"I say"を表示し、続けて引数として渡された関数が続けて実行されます。

function func($callback){
  echo "I say :".$callback();
}

3、コールバック関数を実行しよう

では、"I say"と表示し、その後続けて実行する関数は何か。
1で用意した、callback_func関数を引数として渡してみます。
callback_func関数は"hello"と表示する関数でした。
つまり、"I say"の後に"hello"が表示されます。
(phpではドットで文字列を結合できます)

func("callback_func"); //I say hello

まだまだ、コールバック関数は奥が深そうなので、引き続き色々調べてみます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ハイフン無しの電話番号をハイフン付の形式に変換する

ハイフン無しの電話番号をハイフン付の形式に変換する

  • ハイフン付き電話番号に分割
  • ハイフン無しの電話番号をハイフン付の形式に変換

Installation

You can install this plugin with Composer.

$ composer require rebib/phonenumbersplitter

 $provider = (new Rebib\Phonenumber\PhonenumberSplitter())->normalize("031-234-5678");

ハイフン無し

 echo $provider->getNumberWithoutHyphen(); 
0312345678 

ハイフン付

 echo $provider->getNumberWithHyphen(); 
03-1234-5678

array

 print_r($provider->toArray());
Array
(
    [0] => 03
    [1] => 1234
    [2] => 5678
)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

電話番号をハイフン区切りに変換する

ハイフン無しの電話番号をハイフン付の形式に変換する

  • ハイフン付き電話番号に分割
  • ハイフン無しの電話番号をハイフン付の形式に変換

Installation

You can install this plugin with Composer.

$ composer require rebib/phonenumbersplitter

 $provider = (new Rebib\Phonenumber\PhonenumberSplitter())->normalize("031-234-5678");

ハイフン無し

 echo $provider->getNumberWithoutHyphen(); 
0312345678 

ハイフン付

 echo $provider->getNumberWithHyphen(); 
03-1234-5678

array

 print_r($provider->toArray());
Array
(
    [0] => 03
    [1] => 1234
    [2] => 5678
)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで、view('target.view', ['id' => $id, 'name' => $name]) を少しでもスマートに書きたい

はじめに

皆さんはLaravelライフをエンジョイしてますか?
私は職場でのLaravel歴が長いことから、公式ドキュメントに書いていないような方法で実装を勧めていることが多いです。
Laravel初心者の方には理解に苦しむようなコードを書くことも多く、普段からチームメンバの力量に合わせて適材適所で力を発揮しています。(キリッ)

さて、今回はLaravelでビューにパラメータを渡す際の小技を、初心者の方や使い慣れてきた方向けに紹介していきたいと思います。

公式ページに記載されているビューへのパラメータの渡し方

例1
// ビューへ渡すパラメータを事前に連想配列として準備し…
$user = \App\User::first();
$data['user'] = $user;

// ビューを指定する際に引数として渡す
return view('hello', $data);
例2
// ビューへ渡すパラメータを事前に準備し…
$user = \App\User::first();

// ビューを指定する際に連想配列として渡す
return view('hello', ['user' => $user]);

どうです?イケてますか?連想配列をわざわざ作るコードを書くのは辛いと思いますが…。
私の身の回りの開発者の中でも、特にPHP初心者の方はこの書き方で書いていることが多いです。
上記の例では$userのみのため、まだましな方だと思います。

でも過去には、以下のようなコードを書いていた人もチームメンバに遭遇したことがあります。

カオスコード1
$data['a'] = $a;
$data['b'] = $b;
$data['c'] = $c;
$data['d'] = $d;
$data['e'] = $e;

return view('hello', $data);
カオスコード2
return view('hello', [
    'a' => $a,
    'b' => $b,
    'c' => $c,
    'd' => $d,
    'e' => $e,
]);

いやだ!!、こんな書き方はしたくない!
そもそも変数名と同じキーを指定する連想配列を定義するなんてイケてない!

連想配列をわざわざ作るコードなんて書きたくない場合

compact関数を使うパターン
// ビューへ渡すパラメータを事前に準備し…
$user = \App\User::first();

// ビューのパラメータを指定するタイミングに一気に連想配列を作成する
return view('hello', compact('user'))

私の場合は上記のようにPHP標準のcompact関数を利用し、見るからにイケてないコードを意図的に排除しています。
このように書くことで少しはスッキリ感じないでしょうか?
それによりカオスコードが少しは許容できるような見た目になります。

許容できるようになったカオスコード2
return view('hello', compact('a','b','c','d','e'));

またビューのパラメータは連想配列で渡すことになってますが、実はオブジェクトを渡す方法もあります。
この方法については後日新しい記事として取り扱う予定です。

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】PDOクラスのインスタンス時の例外処理

PDOとは?

PHP Data Objectsの略称で、PHP標準(5.1.0以降)のデータベース接続クラスのことです。

例外処理とは

予め例外が発生しそうな処理を予期し、発生したらスローという形で送られるのでキャッチで処理を行う。

try-catch

例外が発生するかもしれない処理をtryに記述する
例外が発生した場合の処理をcatchに記述する

if文との違い

if文 は条件分岐で、 try は例外時のアクションの規定です。
if でその時点での条件を調べて処理を分岐させます。
trueかfalseどちらに進んでも逐次的な動作(上から順に動く)です。
try-catchは後続の処理が継続不可となる事態が発生した場合は処理を中断させる。

コード例

function hogePDO()
{
    try {
        // PDOクラスのインスタンス化を実行しているがエラーが発生するかもしれない
       return new PDO(DSN, DB_USER, DB_PASSWORD);
    } catch (PDOException $e) {
        // 指定されたデータベースへの接続に失敗した場合の処理
        // PDOExceptionクラスがスローされる、それをキャッチする処理
        // $eはPDOExceptionクラスをインスタンスした物
        // $eからgetMessageメソッドを実行している
        echo $e->getMessage();
        exit();
    }
}

備考

  • Exception頭文字をとって「$e」とするのが慣習となっている

公式

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】PDOクラスインスタンス時の例外処理

PDOとは?

PHP Data Objectsの略称で、PHP標準(5.1.0以降)のデータベース接続クラスのことです。

例外処理とは

予め例外が発生しそうな処理を予期し、発生したらスローという形で送られるのでキャッチで処理を行う。

try-catch

例外が発生するかもしれない処理をtryに記述する
例外が発生した場合の処理をcatchに記述する

if文との違い

if文 は条件分岐で、 try は例外時のアクションの規定です。
if でその時点での条件を調べて処理を分岐させます。
trueかfalseどちらに進んでも逐次的な動作(上から順に動く)です。
try-catchは後続の処理が継続不可となる事態が発生した場合は処理を中断させる。

コード例

function hogePDO()
{
    try {
        // PDOクラスのインスタンス化を実行しているがエラーが発生するかもしれない
       return new PDO(DSN, DB_USER, DB_PASSWORD);
    } catch (PDOException $e) {
        // 指定されたデータベースへの接続に失敗した場合の処理
        // PDOExceptionクラスがスローされる、それをキャッチする処理
        // $eはPDOExceptionクラスをインスタンスした物
        // $eからgetMessageメソッドを実行している
        echo $e->getMessage();
        exit();
    }
}

備考

  • Exception頭文字をとって「$e」とするのが慣習となっている

公式

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP Slim 環境構築(14) ECS(手動で設定するEC2編)

PHP Slim 環境構築(14) ECS(手動で設定するEC2編)

Introduction

前回は、EC2上にNginxとPHP-FPMの両方を格納したDockerコンテナを構築し、AWSの各種サービス(RDB、DynamoDB、S3)とアクセスさせてみました。
今回は、同じDockerコンテナをECS上に構築します。

この一連のシリーズは、自分への備忘録が第一目的のため、だいぶ不親切です。
申し訳ございません。

変更点

ソースコードの変更は、Dockerfileのみです。
このシリーズで構築した環境では、Dockerfileのパス(/compose/web_hoge)とソースコード(/src/hogeや/src/vendor)が離れており、docker-composeのvolumesを使ってコンテナにソースを組み込んでいます。

ソースツリー
$(PROJECTROOT)
  /compose
    /web_hoge
      Dockerfile
  /src
    /hoge
    /vendor
docker-compose.yml
  web_hoge:
    build:
      context: ./web_hoge
    volumes:
      - ../src/hoge:/var/www/hoge
      - ../src/vendor:/var/www/vendor
      - ../credentials:/usr/local/etc/myapp/credentials

Dockerfileの変更

Dockerfileでは、作業ディレクトリよりも親への参照ができません。(例: COPY ../../src/web_hoge など)
したがって、 docker build -f compose/web_hoge/Dockerfile . のように-fオプションで子ディレクトリ内のDockerfileを指定することになります。これに伴い、Dockerfile内のファイル参照のパスも変更されます。
具体的な変更箇所は、COPYコマンドのコピー元が$(PROJECTROOT)からの相対パスで記述されるようになったことと、docker-composeのvolumesの代わりにソースをコピーするようになったことです。

Dockerfile
FROM alpine:3.10.2

ARG environment

..(略)..

# supervisord
RUN mkdir -p /etc/supervisor.d
COPY compose/web_hoge/supervisord.conf /etc/supervisord.conf

# settings
COPY compose/web_hoge/settings-${environment}.yml /usr/local/etc/myapp/settings.yml

# PHP
COPY compose/web_hoge/php.ini /php.ini
COPY compose/web_hoge/php-fpm.conf /php-fpm.conf
COPY compose/web_hoge/php-fpm-supervisor.conf /php-fpm-supervisor.conf
RUN cp /php-fpm.conf /etc/php7/php-fpm.d/z-php.conf \
  && cp /php.ini /etc/php7/conf.d/config.ini \
  && cp /php-fpm-supervisor.conf /etc/supervisor.d/php-fpm.conf

# Nginx
COPY compose/web_hoge/nginx.conf /nginx.conf
COPY compose/web_hoge/nginx-server.conf /nginx-server.conf
COPY compose/web_hoge/nginx-supervisor.conf /nginx-supervisor.conf
RUN cp /nginx.conf /etc/nginx/nginx.conf \
  && cp /nginx-server.conf /etc/nginx/conf.d/default.conf \
  && cp /nginx-supervisor.conf /etc/supervisor.d/nginx.conf

..(略)..

# ソース追加(以下三行追加)
COPY src/hoge /var/www/hoge
COPY src/vendor /var/www/vendor
COPY credentials /usr/local/etc/myapp/credentials

EXPOSE 3128

ENTRYPOINT ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

AWS

ECR

今回はDockerリポジトリとして、Amazon ECRを使用することにします。
ECRサービスメニューから、リポジトリの作成を選びます。

パラメータ
リポジトリ名 hoge-repo

このリポジトリのURIは、<<AWS-ACCOUNT-ID>>.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-repo になります。

ビルド

適当なEC2上でビルドすることにします。なお、以下の条件が整っているものとします。

  • aws configureで接続設定済み (regionはap-northeast-1)
  • dockerコマンドがインストール済み
  • gitコマンドやファイルコピーなどで最近のソースが取得可能
  • ECRへの操作権限を持っている
# $(aws ecr get-login --no-include-email)
# ACCOUNT_ID=$(aws sts get-caller-identity | grep Account | grep -oE '[0-9]+')
# cd <<PROJECT_ROOT>>
# docker build -t hoge-repo:latest --build-arg environment=devaws -f compose/web_hoge/Dockerfile .
# docker tag hoge-repo:latest $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-repo:latest
# docker push $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-repo:latest

IAM(ロールの作成)

Dockerコンテナの持つロールを定義しておきます。
RDS、DynamoDBなどにアクセスすることからそれらを操作する権限が必要です。
(接続確認を優先したため、権限が大きい(FullAccess)です! 本番注意)。

パラメータ
ロール名 hoge-ecs-user
AWSサービス Elastic Container Service Task
ポリシー AmazonRDSFullAccess, AmazonDynamoDBFullAccess, AmazonS3FullAccess

EC2(ターゲットグループ)

ロードバランサの設定前に、そのターゲットグループを設定しておきます。
ポートは、nginxへのアクセスポートである3128です。

パラメータ
名前 hoge-target
ターゲットの種類 インスタンス
ポート 3128
ヘルスチェック http /favicon.ico

EC2(セキュリティグループ)

以下の二種類のアクセスに対するセキュリティグループを定義します。

  • ロードバランサへのアクセス

外部からのhttp(s)のアクセスです。

パラメータ
名前 hoge-http-sg
インバウンド 80(0.0.0.0/0), 443(0.0.0.0/0)
  • Dockerコンテナへのアクセス

ECSのインスタンスへ直接SSHアクセスするための定義(ポート22)と、ALBからのアクセス(動的ポートマッピングを使用するのでその範囲)。

パラメータ
名前 hoge-instance-sg
インバウンド 22(SSH接続可能なIP), 32768-65535(ロードバランサのVPC)
  • AWS Certificate Manager

SSLを使う場合は、こちらで証明書を発行します。発行後、承認されるまでは最大72時間かかるので注意が必要です
(詳細は本筋と外れるので省略)。

  • EC2(ロードバランサ)

ALBの設定を行います。

パラメータ
名前 hoge-alb
スキーム インターネット向け
リスナー 80, 443(証明書が必要です)
AZ ap-northeast-1a, 1c, 1d全部
セキュリティグループ hoge-http-sg
ターゲットグループ hoge-target
  • ECS(クラスター)

ECS用のクラスターを作成します。なお、この設定を行った際に、自動的にEC2(Auto Scalingグループ)とEC2(起動設定)が作成されます。

パラメータ
クラスターテンプレート EC2 Linux + ネットワーキング
クラスター名 hoge-cluster
プロビジョニングモデル オンデマンド
EC2インスタンス t2.micro
インスタンス数 1
キーペア 直接インスタンスにアクセスする場合は設定する。本番環境等ではなし
VPC 既存のVPC
サブネット ap-northeast-1a, 1c, 1dの全部
セキュリティグループ hoge-instance-sg
コンテナインスタンスIAMロール ecsInstanceRole(自動生成される)
  • EC2(起動設定)

クラスターを生成したときに自動生成された起動設定をコピーし、修正します。
ユーザーデータでは、サーバに必要な設定の変更なども行います (ここに示しているのはあくまでサンプルです)。

パラメータ
名前 hoge-launch-config
IAMロール ecsInstanceRole
モニタリング 詳細モニタリングを無効に
ユーザーデータ (下記参照)
IPアドレスタイプ 直接インスタンスにアクセスする場合は割り当てる。本番環境等では割り当てない
ユーザーデータ
#!/bin/bash
cat <<'ECS_EOF' >> /etc/ecs/ecs.config
ECS_CLUSTER=hoge-cluster
ECS_BACKEND_HOST=
#ECS_LOGLEVEL=debug
ECS_EOF

cat <<'SYS_EOF' >> /etc/sysctl.conf
net.core.somaxcon=65535
SYS_EOF

sysctl -p /etc/sysctl.conf

cat <<'FLIMIT_EOF' > /etc/security/limits.d/filelimits.conf
* soft nofile 32768
* hard nofile 32768
FLIMIT_EOF

ulimit -n 32768
  • EC2(Auto Scalingグループ)

クラスターを生成したときに自動生成されたオートスケールグループ設定をコピーし、修正します。

パラメータ
起動設定 hoge-launch-config
  • ECS(タスク定義)

ECSで生成されたインスタンスにDockerコンテナを載せるためのタスク定義を行います。

パラメータ
起動タイプ EC2
タスク定義名 hoge-task
タスクロール hoge-ecs-user
ネットワークモード
タスク実行ロール ecsTaskExecuteRole(自動生成される)
タスクメモリ なし (本当はちゃんと設定する)
タスクCPU 1024 (本当はちゃんと設定する)
コンテナ名 hoge-container
イメージ <<AWS-ACCOUNT-ID>>.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-repo:latest
メモリ制限 ハード/300MB (本当はちゃんと設定する)
ポートマッピング ホスト(0) : コンテナ(3128) (自動ポートマッピングを使用するのでホストポートは0)
  • ECS(サービス追加)

タスクを元にECSにサービスを追加します。

パラメータ
起動タイプ EC2
タスク定義 hoge-task
クラスタ hoge-cluster
サービス名 hoge-service
サービスタイプ REPLICA
タスクの数 1
デプロイ ローリングアップデート
ロードバランサー hoge-alb
サービス用IAMロール AWSServiceRoleForECS
ロードバランス用コンテナ hoge-container:0:3128
サービスの検出の統合の有効性 無効
ロードバランス用のコンテナターゲット hoge-target
AutoScaling 無効

接続確認

ここまで完了すれば、うまく動いている(はず)です。
うまくいっていれば、以下のURLにアクセスすれば"BEFORE:Hello, World!:AFTER"が表示されます。
SSL接続するケースや、独自ドメインを使用するケースはここでは割愛します。

http://hoge-alb-<<ALBに割り当てられたID>>.ap-northeast-1.elb.amazonaws.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでlz4を使う

はじめに

PHPでlz4を使いって解凍したいという要望が社内で出た。
ただ、日本語の資料があまりにもないので個人的に備忘録としてまとめる。

書くこと

  • PHPが動いてるサーバにlz4を入れて動かす方法

書かないこと

  • PHP環境のサーバの構築

lz4とは?

簡単に言えば優秀な圧縮ツール。

手順

$ yum install lz4
$ yum install lz4-devel

$ yum installed list | grep lz4

# lz4.x86_64                            1.7.5-3.el7                    @base
# lz4-devel.x86_64                      1.7.5-3.el7                    @base

$ yum --enablerepo=remi install php-lz4

これで基本的に必要なものは入る。

$ sudo systemctl restart php-fpm

これでプログラム内からlz4_compress()等の関数が呼び出せるようになる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP7.0 × Zend Framework1で作るMVCモデルWebアプリケーション

の、超導入です。

1.PHPのインストール -XAMPP

XAMPP = ザンプ

X:クロスプラットフォーム
A:Apatch
M:MariaDB/MySQL
P:PHP
P:Perl

PHPを用いたWebアプリケーションに必要なソフトウェア群を一度にインストールできるパッケージ。
公式サイトからダウンロード、インストール。(Nextをポチポチしていれば問題ないはず)
完了したらXAMPPのコントロールパネルが表示される。ここで各ソフトウェアの起動・終了を行う。

赤いエラーが出ている場合
使用するポート番号が既に使用されている可能性。
https://norakura-jyoko.com/xampp-local-setup を参照

2.Zend Framework1を導入する

最新版は3だが業務の都合で1を入れた。

だいたいこの通りにやればできる。
http://wiki.tmd45.jp/wiki.cgi?page=XAMPP%A4%CBZend+Framework%A4%F2%C6%B3%C6%FE

Webアプリケーションを作成する

ZendFramewodkの推奨ディレクトリ構造
https://framework.zend.com/manual/1.12/ja/project-structure.project.html

最小のディレクトリ構成

<project name>/
    <application>/
        <controllers>/
            IndexController
        <views>/
            index.phtml
        <models>/
    index.php
    .htaccess

フロントコントローラの設定

上記のディレクトリ構造を作成したのち、

:.htaccess
RewriteEngine on
RewriteBase /
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php

ベースURL以下のリクエストをすべてフロントコントローラに飛ばす設定。
Apacheの組み込みモジュールmod_rewriteのリライト機能を使用している。

Zend Frameworkでは、ここで設定したベースURlから、その下の階層をコントローラ名、さらに下の階層をアクション名とする。
ex) https://server-name/fuga/hoge → コントローラ名がfuga,アクション名がhogeとなる。
(コントローラとアクションについては後述)

RewriteBase /fugaとしていた場合には、上記アドレスはhogeコントローラのindexアクションを指す。

index.php
<?php
    require_once 'Zend/Controller/Front.php';
    Zend_Controller_Front::run('application/controllers');

フロントコントローラの設定。

コントローラとアクション

Zend FrameworkではURL構造を利用してルーティングを行う。

例1:https://server-name/fuga/hoge の場合

→プログラムはFugaController.phphogeActionメソッドを実行する。

例2:https://server-name/fuga の場合

→プログラムはFugaController.phpindexActionメソッドを実行する。

例3:https://server-name/ の場合

→プログラムはIndexControllerindexActionメソッドを実行する。

アクションとビュー

controllers/IndexController.php
<?php
 require_once 'Zend/Controller/Action.php';

 class IndexController extends Zend_Controller_Action{
    public function indexAction(){
        echo $this->render();
    }
 }
views/index/index.phtml
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Zend Framework テスト</title>
</head>
<body>
    <h1>Hello, Zend Framework!</h1>
</body>
</html>

FugaControllrのhogeActionメソッドは、views/fuga/hoge.phtmlに対応する。

$this->render()とした場合はデフォルトで対応するviewが描画される。

アクションからビューへ値を渡す

FugaController.php
<?php
 require_once 'Zend/Controller/Action.php';

 class IndexController extends Zend_Controller_Action{
    public function indexAction(){
        $this->view->assign($_POST);
        echo $this->render();
    }
 }

$this->view->assign(変数名);によって変数をビューに渡す。

エラーハンドリング

Zend Frameworkではエラーが発生したとき、自動的にErrorControllerのerrorActionを実行する。

ErrorController.php
<?php echo '<?php'; 

require_once 'ZErend/Controller/Action.php'; 
require_once 'Zend/Controller/Plugin/ErrorHandler.php'; 

class ErrorController extends Zend_Controller_Action 
{ 
    public function errorAction() 
    {   
        $errors = $this->_getParam('error_handler'); 

        switch ($errors->type) { 
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE: 
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER: 
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION: 
                // コントローラもしくはアクションが存在しない場合 
                $msg = 'リクエストされたコントローラ・アクションは存在しません。'; 
                $this->view->assign('errors',$msg);
                break; 
            default: 
                // アプリケーションの処理中に発生したエラーの処理 
                $msg = $errors->exception->getMessage(); 
                $this->view->assign('errors',$msg);
                break; 
        }   
    }   
}
/views/error/error.phtml
<!DOCTYPE HTML>
<html lang="ja"> 
<head> 
    <meta charset="UTF-8"> 
    <title>Error!</title> 
</head> 
<body> 
    <?=$this->errors ?>
</body> 
</html>

PDOを使用したデータベース接続

https://qiita.com/mitsuru793/items/45b2452284e321c7a5a9

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】名前空間とトレイトを同時に使用する場合

PHP初心者、初級者だと初めて聞く存在だと思いますが、PHPには名前空間、トレイトという仕組みも実装されています。

では、今更ではあるのですが、この名前空間とトレイトを同時に使う場合(要は名前空間ごとに、同一名称のトレイトが存在する場合です。実務的な機会があるのかどうかはわかりませんが…)はどうすればいいのかを調査してみました。すると、色々と注意する部分があるようなので、備忘録としてプログラムと解説を置いておきます。

なお、本記事はオブジェクト指向を熟知しているPHP中級者以上を前提としています。また、名前空間、トレイトを知らない人は、参考記事を熟読しておいてください。

実例

【要件定義】各都道府県の果物と野菜をそれぞれ調べたい。名前空間にはそれぞれfruits、vegitableと定義され、その中に各地方の名称となったトレイト(kanto、kinkiなど)があり、その中に各都道府県のメンバ(kantoならgunma()、tochigi()など)が用意されている。

つまり、このような状態です。トレイト名が同じために、名前空間で定義している状態になります。また、トレイト内も同じ名称のメンバが入っていますが、それぞれ用意されている値は異なっています。

PHP
    namespace fruits
    {
        trait Kanto
        {
            public function tochigi(){
                $this -> fruits = "いちご";
            }

            public function gunma(){
                $this -> fruits = "りんご";
            }
        }
    }

    namespace vegitable
    {
        trait Kanto
        {
            public function tochigi(){
                $this -> vegi = "かんぴょう";
            }

            public function gunma(){
                $this -> vegi = "キャベツ";
            }
        }
    }

メンバのエイリアス指定

ここで気をつけないといけないのは、トレイト名ならびにメンバ名が同一であるため、このままブラウザで表示しようとするとトレイトの規約違反でコリジョンエラー(同じトレイト名やメンバー名にしていると、何を呼び出していいのかプログラムで判断できない)を起こしてしまいます。そこで、各メンバに対しエイリアスを指定する必要があります。

→ [PHP] こんなときどうする?trait編

これを参考にして、以下のようにメンバ名を別名に定義します。
まずは、名前空間の外側を無名の名前空間で囲みます(名前空間の外側だとエラーになります)。
そして別名のメンバを必ずトレイト内のメンバとオーバーライドさせるようにします。

→ オーバーライドについて

PHP
    namespace
    {
        class Crops
        {
            //public $fruits;
            //public $vegi;
            //トレイト内で重複するメンバがないようにすること
            use fruits\kanto, vegitable\kanto{
                fruits\kanto::tochigi as f_tochigi;
                vegitable\kanto::tochigi as v_tochigi;
                fruits\kanto::gunma as f_gunma;
                vegitable\kanto::gunma as v_gunma;
            }
            //必ずメンバをオーバーライドさせること。独自に定義した別メンバ名だとエラーになる
            public function tochigi(){
                $this -> f_tochigi();
                $this -> v_tochigi();
            }

            public function gunma(){
                $this -> f_gunma();
                $this -> v_gunma();
            }
        }
    }

エイリアスの定義

これだと、名前空間が煩雑なので、すっきりさせるためにエイリアスを定義します。

PHP
    namespace
    {
        //名前空間のエイリアス定義はクラスの外側で行う
        use fruits\kanto as f_kanto;
        use vegitable\kanto as v_kanto;
        class Crops
        {
            //public $fruits;
            //public $vegi;
            //トレイト内で重複するメンバがないようにすること
            use f_kanto, v_kanto{
                f_kanto::tochigi as f_tochigi;
                v_kanto::gunma as v_tochigi;
                f_kanto::tochigi as f_gunma;
                v_kanto::gunma as v_gunma;
            }
            //必ずメンバをオーバーライドさせること
            public function tochigi(){
                $this -> f_tochigi();
                $this -> v_tochigi();
            }

            public function gunma(){
                $this -> f_gunma();
                $this -> v_gunma();
            }
        }
    }

ゲッタの設定

値を引き出せるようにゲッタをクラス内に設定します。

PHP
    namespace
    {
        //名前空間のエイリアス定義はクラスの外側で行う
        use fruits\kanto as f_kanto;
        use vegitable\kanto as v_kanto;
        class Crops
        {
            public $fruits;
            public $vegi;
            //トレイト内で重複するメンバがないようにすること
            use f_kanto, v_kanto{
                f_kanto::tochgi as f_tochigi;
                v_kanto::gunma as v_tochigi;
                f_kanto::tochigi as f_gunma;
                v_kanto::gunma as v_gunma;
            }
            //必ずメンバをオーバーライドさせること
            public function tochigi(){
                $this -> f_tochigi();
                $this -> v_tochigi();
            }

            public function gunma(){
                $this -> f_gunma();
                $this -> v_gunma();
            }
            //ゲッタの指定(共通でデータを抽出できる)
            public function getter(){
                $fruits = $this -> fruits;
                $vegi = $this -> vegi;
                return [$fruits,$vegi];
            }

        }
    }

インスタンスからメンバの値を取り出す

あとはインスタンスを指定して、メンバにアクセスすればデータを取得できます。

PHP
        $Crops = new Crops(); //インスタンス生成
        $Crops -> tochigi(); 
        list($fruits,$vegi) = $Crops -> getter();
        echo "【栃木】果物:{$fruits}、野菜:{$vegi}<br>";
        $Crops -> gunma();
        list($fruits,$vegi) = $Crops -> getter();
        echo "【群馬】果物:{$fruits}、野菜:{$vegi}<br>";

ブラウザなどでレビューをかけると

【栃木】果物:いちご、野菜:かんぴょう
【群馬】果物:りんご、野菜:キャベツ

このように表示させることができます。

全体の構造

備忘録として作成した原本プログラムはこのようになっています。トレイトの中はkinkiも入っており、メンバの中身もトレイトごとに違いますが、動作は全く同じです。また、トレイトやメンバの中をどんどん増やしていくことができます。その際には必ず逐一エイリアスも指定して、コリジョンを起こさないようにしてください。

PHP
<?php
    namespace fruits
    {
        trait Kanto
        {
            public function tochigi(){
            $this -> fruits = "いちご";
            }

            public function gunma(){
            $this -> fruits = "りんご";
            }
        }

        trait Kinki
        {
            public function wakayama(){
                $this -> fruits = "うめ";
            }

            public function nara(){
                $this -> fruits = "かき";
            }
        }
    }

    namespace vegitable
    {
        trait Kanto
        {
            public function tochigi(){
            $this -> vegi = "かんぴょう";
            }

            public function gunma(){
            $this -> vegi = "キャベツ";
            }
        }

        trait Kinki
        {
            public function wakayama(){
                $this -> vegi = "しょうが";
            }

            public function nara(){
                $this -> vegi = "みょうが";
            }
        }
    }

    namespace
    {
        //エイリアスを指定する場合はクラスの外側で
        use  fruits\Kanto as f_kanto;
        use  vegitable\Kanto as v_kanto;
        use  fruits\Kinki as f_kinki;
        use  vegitable\Kinki as v_kinki;
        class Crops
        {
            public $fruits;
            public $vegi;
            //トレイト内で重複するメンバがないようにすること
            use f_kanto{
                f_kanto::tochigi as f_tochigi;
                f_kanto::gunma as f_gunma;
            }
            use v_kanto{
                v_kanto::tochigi as v_tochigi;
                v_kanto::gunma as v_gunma;
            }
            use f_kinki{
                f_kinki::wakayama as f_wakayama;
                f_kinki::nara as f_nara;
            }
            use v_kinki{
                v_kinki::wakayama as v_wakayama;
                v_kinki::nara as v_nara;
            }
            //必ずメンバをオーバーライドさせること
            public function tochigi(){
                $this -> f_tochigi();
                $this -> v_tochigi();
            }

            public function gunma(){
                $this -> f_gunma();
                $this -> v_gunma();
            }
            public function wakayama(){
                $this -> f_wakayama();
                $this -> v_wakayama();
            }

            public function nara(){
                $this -> f_nara();
                $this -> v_nara();
            }

            public function getter(){
                $fruits = $this -> fruits;
                $vegi = $this -> vegi;
                return [$fruits,$vegi];
            }
        }

        $Crops = new Crops();
        $Crops -> tochigi();
        list($fruits,$vegi) = $Crops -> getter();
        echo "【栃木】果物:{$fruits}、野菜:{$vegi}<br>";
        $Crops -> gunma();
        list($fruits,$vegi) = $Crops -> getter();
        echo "【群馬】果物:{$fruits}、野菜:{$vegi}<br>";
        $Crops -> wakayama();
        list($fruits,$vegi) = $Crops -> getter();
        echo "【和歌山】果物:{$fruits}、野菜:{$vegi}<br>";
        $Crops -> nara();
        list($fruits,$vegi) = $Crops -> getter();
        echo "【奈良】果物:{$fruits}、野菜:{$vegi}<br>";
    }
【栃木】果物:いちご、野菜:かんぴょう
【群馬】果物:りんご、野菜:キャベツ
【和歌山】果物:うめ、野菜:しょうが
【奈良】果物:かき、野菜:みょうが
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

XMLデータを連想配列に変換する手順

WordListの中にあるFuriganaデータを1つの変数に格納するため、Yahooのかな文字変換APIを利用する際に、返却されたデータがXMLだったので扱いやすいように連想配列にした。

https://developer.yahoo.co.jp/webapi/jlp/jim/v1/conversion.html

今回はGuzzleを使った。

  1. APIからデータを取得
  2. simplexml_load_stringでXMLデータをオブジェクトに変換
  3. json_encodeを使ってオブジェクトからJSONに変換
  4. json_decodeに第2引数にtrueを指定する事で連想配列に変換
xml.php
//APIからデータを取得
$xml = $httpClient->get(self::API_BASE_URL, [
                    'query' => $query
                ])
                ->getBody()
                ->getContents();                

// XMLデータをオブジェクトに変換
$object = simplexml_load_string($xml);

//オブジェクトをJSONに変換してから、連想配列に変換
$array = json_decode(json_encode($obj), true);

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

表面的なComposerの依存について

目次

  • composerとは?
  • 依存について
  • install・updateについて

composerとは?

PHPのパッケージ管理システムである

できること

  • gitやsvn上のPHPライブラリ(パッケージ)を依存として定義&管理(インストールやバージョン確認)
  • クラス・インターフェイスの自動ロード(オートローディング)

今回メインで説明するのは上の方。

依存について

composer使う理由として、パッケージの依存管理が容易になるというのはかなりあると思うのですが、別にcomposer使わなくてもパッケージ依存管理はできるわけで、、、
使用しない場合と使用する場合でどれだけ違うかというと以下。

composerを使用しない場合とする場合

使用しない場合
composerを使用しない場合、必要なパッケージ(PHPライブラリ)をインストールするために、各パッケージが管理されているリポジトリ(gitやsvn)にアクセスし、zipファイル等をローカルにダウンロードし、それらをローカルで展開し、vender/以下に配置する。
仮に、パッケージが他のパッケージに依存している場合、依存しているパッケージに対しても上と同じことを行う。

使用する場合
composerを使用する場合、必要なパッケージ(PHPライブラリ)をインストールするために、composer.jsonに文法通り(公式日本語記事を参考)の書き方でパッケージ名とバージョンを記載するだけで、上記で行ったことを全てcomposer側がよしなにやってくれる。
仮に、パッケージが他のパッケージに依存していた場合でも、composerが自動で依存解決を行い、自動で必要なパッケージを取得し&vernder/以下に配置してくれる。

まとめ
上で見ても分かるように、composerなしでパッケージをインストールしたり、依存解決を行うのは、莫大な時間がかかり、普段の業務レベルで言うと、ほぼ不可能に近いので、基本的にパッケージ管理を行う際は、composerを使うことになる。
では、実際にcomposerを使ってパッケージを管理するためにはどうすればいいのか??
ここで登場するのがinstall・updateコマンド。

install・updateについて

  • installは、パッケージを取得する。
  • updateは、パッケージのバージョン更新する。

簡単に言えばこんな感じ。
さすがに簡単に言い過ぎたので、ちょっとだけ踏み込んでみると、、、

install

まず、composer.lockの存在チェックを行う。

存在する場合
composer.lockに記載されている、全パッケージを指定通りのバージョンで取得する。

存在しない場合
composer.jsonの「require」に記載されているバージョンのパッケージと依存している(そのパッケージが動くために必要な)パッケージがPackagistというcomposerで取得できるパッケージを管理している中央リポジトリに存在するか見に行き、存在すれば取得、存在しなければcomposer.jsonの「repositories」に登録されているリポジトリを見に行き、取得する。
パッケージ取得後、composer.lockを作成し、取得したパッケージと依存パッケージのバージョン情報を記載して保存する。

update

composer.jsonの「require」に記載のパッケージをPackagist若しくはcomposer.jsonの「repositories」記載のリポジトリに見に行き、最新のバージョンが存在すれば、そのパッケージと依存パッケージを最新のバージョンに更新する。
取得パッケージのバージョンに変更があった場合、変更に合わせてcomposer.lock記載のパッケージのバージョンも変更される。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【64日目】echo true ? 1 : false ? 2 : 0; 正しく計算できますか? ーPHPの結合規則と優先順位について

突然ですがPHP駆け出しエンジニアのみなさま!
タイトルの問題、正しく計算できるでしょうか?
私は初見は引っかかってしまいました。

答えは「2」です。

正しく計算できた方、なぜそうなるかきちんと説明できますか?

この記事を読んで、明日からは「演算子」について自信をもってコードを書けるようになりましょう!

1 演算子

1.1 演算子とは

演算子とは「値に対して行ってなにがしか別の値を生み出すような操作」のことです。
簡単に言えば、値を変化させるような操作すべてを表します。
PHPでは単項、二項、三項演算子が扱われ、タイトルの問題は三項演算子が「ネスト(入れ子)」になっているものです。

1.2 演算子の種類と優先順位

ここでは演算子の詳細な解説は省きます。知らないものがあった場合はグーグル先生に聞いてください。
演算子には、種類によって結合法則と優先順位が異なります。
それぞれの語句の意味を説明したうえで、結合法則と優先順位をまとめたマトリクスを提示します。

1.2.1 結合法則

結合法則とは、2つの値を演算子を用いて演算する際に、右辺と左辺どちらから評価されるのかを決める法則である。
結合法則には左結合と右結合と、結合しないものとに分類される。
特に、PHPの場合、他の言語と異なり三項演算が左結合となることに留意が必要である。

PHPでは多数派は左結合となるので、右結合と結合しない例のみを列挙しておく。

1.2.1.1 右結合の演算子の一覧

  • (int)など、@ キャスト、エラー制御演算子
  • ! 論理演算子
  • =, +=, -=, *=, /=, .=, %=, &=, |=, ^=, <<=, >>=, => 代入演算子

1.2.1.2 結合なしの演算子の一覧

  • new, clone オブジェクトの生成、複製
  • ++, -- 加算子、減算子
  • instanceof 型
  • <, <=, >, >=, <> 比較演算子
  • ==, !=, ===, !== 比較演算子

1.2.2 優先順位

演算子の優先順位とは、複数の演算子が連続した場合に、どの演算子から先に評価していくかを定めたルールである。
例えばイメージしやすいものであれば、同じ代数演算子でも*と/は+や-よりも優先順位が高い。
いくつもの演算子が用いられる場合や、ネストで式を記述する際には、わかりやすくするために()を明示的に用いて優先順位を示すのも良い。

例えば

2 * 3 + 5

であれば

((2 * 3) + 5)

のような意味です。

1.3 演算子の結合法則と優先順位の表

上から順に優先順位が高く評価される演算子となります。

結合法則 演算子 説明
結合しない new, clone オブジェクトの生成、複製
右結合 [ 配列のブラケット
結合しない ++, -- 加算子、減算子
右結合 ~, -, (int)など, @ キャスト、エラー制御演算子
結合しない instanceof
右結合 ! 論理演算子
左結合 *, /, % 代数演算子
左結合 +, -, . 代数演算子、文字列演算子
左結合 <<, >> ビット演算子
結合しない <, <=, >, >=, <> 比較演算子
結合しない ==, !==, ===, !== 比較演算子
左結合 & ビット演算子、 参照
 左結合 ^ ビット演算子 
左結合
左結合 && ビット演識
左結合  || 論理演算子
左結合 ?, : 三項演算子
右結合 =, +=, -=, *=, /=, .=, %=, &=, |=, ^=, <<=, >>=, 代入演算子
左結合 and 論理演算子
左結合 xor 論理演算子
左結合 or 論理演算子
左結合 .

こう見ると、同じ種類の演算子の間でも優先順位がつけられていることわかりますね。

2 タイトルの問題の解説

タイトルの問題を再掲します。

echo true ? 1 : false ? 2 : 0;

起きがちな間違いは、単純に左から進めて言って「1」と答えることです。

この問題は、結合法則のみで解くことができます。
結合法則の評に基づき評価される順に()を付けて書き直すと

echo ②(  ①( true ? 1 : false? ) ? 2 : 0);

となります。
まず、三項演算は左結合のため、演算子?、:の左から評価します。

最初①部の処理です。
?の左にあるtrueが評価され、真です。
次に真の場合は:の左側である1を評価し、1となります。

ここまで評価した段階で①括弧内が整理され、この式は

echo ②( ①(1) ? 2 : 0);

と表すことができると思います。
すると、②の挙動とは、
「三項演算子「?」の前にある①(1)を評価し、真ならば2を、偽ならば0」を表示することとなります。
本問では、(1)は真なので、答えは2となります

このように括弧を表示するとネスト構造の演算子はみやすくなります。
思わぬエラーを引き起こさないように気をつけていきましょう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP

php
<html>
<body>

<form action='test.php' method='get'>
  <input type='text' name='txt1'>
  <?php 
    if (isset($_GET['txt1'])) {
      echo  $_GET['txt1'];
    }
  ?>

  <br/>

  <input type='text' name='txt2'>

  <?php 
    if (isset($_GET['txt2'])) {
      echo  $_GET['txt2'];
    }
  ?>
  <br/>
  <input type='submit'>
</form>

<hr/>

<form action='test.php' method='post'>
  <input type='text' name='txt3'>
  <?php 
    if (isset($_POST['txt3'])) {
      echo $_POST['txt3'];
    }
  ?>
  <br/>
  <input type='submit'>
</form>


<?php

$items = [['name' => '吉田', 'age' => 20, 'genderId' => 0], ['name' => '田中', 'age' => 30, 'genderId' => 1]];
$gender = [0 => '男', 1 => '女'];

$tbl = '<table border=1>';
foreach ($items as $i => $val) {
  $tbl .= '<tr><td>' . $val['name'] . '</td><td>' . $val['age'] . '</td><td>' . $gender[$val['genderId']] . '</td></tr>';
}
$tbl .= '</table>';
echo $tbl;

// $html = file_get_contents('https://stocks.finance.yahoo.co.jp/stocks/history/?code=USDJPY=X');
// echo $html;
// var_dump($items);

?>

</body>
</html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む