20190714のPHPに関する記事は15件です。

php-master-changes 2019-07-13

今日は <?php に続いて空白文字が来なかった場合に T_INLINE_HTML を分割しないようにする修正、不要コードの削除、php_error_docref0() の php_error_docref() への置き換え、テストこけの修正、ドキュメントの更新があった!

2019-07-13

nikic: Don't split T_INLINE_HTML at partial PHP tag

cmb69: No more need to cater to pre-PHP-5.3 or PHP-6 versions

petk: Remove tsrm_config_common.h

Girgias: Convert all php_error_docref0 to php_error_docref

cmb69: Fix #78282: atime and mtime mismatch

cmb69: Fix #78283: no output for explicit environment

petk: Remove WARNING_LEVEL fix for QNX

petk: Remove checks for always enabled spl and pcre

petk: Remove unused recode extension artefacts

petk: Remove php_multiple_shlib_versions_ok

petk: Update changelogs

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

Laravelのインストール

自分のパソコンにLaravelの開発環境を作ったので、
コマンド諸々忘れないように記録。

1, composerインストール

 $ brew install homebrew/php/composer

もしこれで、
Error: homebrew/php was deprecated.
This tap is now empty as all its formulae were migrated.

こんなエラーが出たら、
$ brew install homebrew/core/composer

こっちをうってみてください!これでいけます。

2, Laravelのインストール

Composerを使って、Laravelのインストーラーを
ダウンロードします。

$ composer global require "laravel/installer"

そしたらパスを設定します。
$ vi ~/.bashrc

で開いて、中身に
export PATH="$PATH:$HOME/.composer/vendor/bin"
を記述しました。

そしたら読み込みます。
$ source ~/.bashrc

3,プロジェクトを作成する

ここまでできたらプロジェクトを作るのですが、
composerコマンドでプロジェクトを作成します。

$ laravel new {プロジェクト名}

というコマンドはよくない。らしい(理由が追えていない。。調べます。)

$ composer create-project --prefer-dist laravel/laravel {プロジェクト名}

できた!

そしたらサーバを起動しましょう!
$ php artisan serve
Laravel development server started: http://127.0.0.1:8000

アクセスするとLaravelのページが表示されます。
(止める時はcontrol + c)

無事完成です!

まとめ
composerって言うのがしれっと出たけど、
PHPのプロジェクトが必要とするライブラリやパッケージを管理する
「ライブラリ依存管理ツール」で、依存関係の解決に便利みたい。
(例えばAというパッケージの利用にBが必要な場合、
パッケージAを宣言すれば自動的にBインストールしてくれる、
みたいな感じ。これが手作業だとAもBもいちいちインストールしなきゃいけない。
めんどくさいですね。)

ここから色々やっていくぞ〜

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

Raspberry Piで Laravel5.8 PHP7.3 nginx1.14の環境構築

Raspberry Pi 3 Model B+ を購入。Laravelで作成したいアプリケーションがあったため環境構築。
それとpythonスクリプトを使って有機ELにいろいろ表示させるものを作ってみようかと。
とりあえずLaravelインストールまで。

ラズパイということでApacheでなく軽量なnginxにしました。
あまり効率的でない手順かもしれないですがご勘弁を。

[前提]
Raspberry Piセットアップ済み (OSはRaspbian)
あとはvimをインストール、デフォルトエディタ変更とbashrcを自分用に修正したくらいです

とりあえずアップデート

rootユーザからの作業
$ sudo su -

アップデート
# apt-get update

nginxインストール・設定

インストール
# apt-get install nginx

確認
# nginx -v

起動、自動起動設定
##systemctl start nginx
# systemctl enable nginx

ブラウザで表示確認。
http://Raspberry PiのIPアドレス

Welcomeされる。
キャプチャ3.PNG

php-fpmインストール・設定

インストール
# apt-get install php7.3-fpm

nginx + php設定
# vi /etc/nginx/sites-available/default

※44行目あたり (index.php追加)
[変更前]
# Add index.php to the list if you are using php
index index.html index.htm index.ngin-debian.html;
[変更後]
# Add index.php to the list if you are using php
index index.html index.htm index.ngin-debian.html index.php;

※56から63行目のコメントアウト削除 (62行目のfastcgi_passの行のみコメントのまま)
location ~ \.php$ {
…
fastcgi_pass unix:/run/php/php7.3-fpm.sock;
…
# fastcgi_pass 127.0.0.1:9000;
}

PHP設定
# vi /etc/php/7.1/fpm/php.ini
※775行目
[変更前]
;cgi.fix_pathingo=1
[変更後]
cgi.fix_pathingo=0

再起動
# systemctl restart php7.3-fpm
# systemctl enable php7.3-fpm
# systemctl restart nginx

PHP確認用
# echo '<?php phpinfo(); ?>' > /var/www/html/test.php

ブラウザで表示確認。
http://Raspberry PiのIPアドレス/test.php

phpinfo表示されたらOK。「あ」と表示されなくても大丈夫。
キャプチャ2.PNG

Mariaインストール・設定

インストール
# apt-get install php7.3-mysql
# apt-get install mariadb-server

確認
# mysqld -V
# mysql -V

設定
# mysql_secure_installation
※rootパスワード設定、他は基本 Y+Enter

管理者権限でログインできるよう設定
# mysql -u root
> use mysql;
> update user set plugin='' where user='root';
> flush privileges;
> quit;

先ほど設定したパスワードでログイン確認
# mysql -uroot -p

自動起動しておく
systemctl enable mariadb

Laravel環境セットアップ

Composerでやります
# apt-get install composer

確認
# composer -V

ディレクトリ作成
# mkdir /var/www/laravel

↓自分だけの開発環境のため安易に済ませている。
# chmod 777 /var/www/laravel

project作成に必要
# apt-get install php-xml

piユーザに戻る(もしくはLaravel実行ユーザ)
# exit

project作成(ちょっと時間かかる)
$ composer create-project laravel/laravel /var/www/laravel

Laravelバージョン確認
$ cd /var/www/laravel/
$ php artisan --version

rootに戻る
$ sudo su - 

DocumentRoot設定を書き換え
# vi /etc/nginx/sites-available/default
※41行目以下に変種

#       root /var/www/html;
        root /var/www/laravel/public;

nginx再起動
# systemctl restart nginx

failed to open stream対策
↓自分だけの開発環境のため安易に済ませている。
# chown -R www-data:www-data /var/www/laravel/storage
# chmod -R 757 /var/www/laravel/storage

ブラウザ確認
ブラウザで表示確認。
http://Raspberry PiのIPアドレス

白いのが表示されたらOK
キャプチャ.PNG

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

macOS Mojaveで最新のPHP(7.3)にVersion upする

背景

なんてことはないけど、エラーが多発したので備忘録として

操作

Homebrewはインストールしている前提

$ brew search php@7
$ brew install php@7.3

Pythonインストール時にerrorがでる

An unexpected error occurred during the `brew link` step
The formula built, but is not symlinked into /usr/local
Permission denied @ dir_s_mkdir - /usr/local/Frameworks

以下のコマンド実行

$ sudo mkdir /usr/local/Frameworks
$ sudo chown $(whoami):admin /usr/local/Frameworks
$ brew link python

もう一度

$ brew search php@7
$ brew install php@7.3

今度は

Error: The `brew link` step did not complete successfully
The formula built, but is not symlinked into /usr/local
Could not symlink sbin/php-fpm
/usr/local/sbin is not writable.

You can try again using:
  brew link php

Pythonインストール時のエラー同様に以下を実行

$ sudo mkdir /usr/local/sbin
$ sudo chown $(whoami):admin /usr/local/sbin
$ brew link php

以下のメッセージがでる

If you need to have php@7.3 first in your PATH run:
  echo 'export PATH="/usr/local/opt/php@7.3/bin:$PATH"' >> ~/.bash_profile
  echo 'export PATH="/usr/local/opt/php@7.3/sbin:$PATH"' >> ~/.bash_profile

指示通り

$ echo 'export PATH="/usr/local/opt/php@7.3/bin:$PATH"' >> ~/.bash_profile
$ echo 'export PATH="/usr/local/opt/php@7.3/sbin:$PATH"' >> ~/.bash_profile
$ brew services start php

ターミナルを再起動してPHPのVersion確認

$ php -v

以下のメッセージが出れば成功

PHP 7.3.7 (cli) (built: Jul  5 2019 12:44:05) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.7, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.7, Copyright (c) 1999-2018, by Zend Technologies

念の為 phpinfoを確認

$ touch index.php
$ echo "<?php phpinfo(); ?>" >> index.php
$ php -S localhost:8000

スクリーンショット 2019-07-14 15.59.42.png

がphpinfoでも出ればOK.

参考

MacのPHPのバージョンを 7.3 に上げた

Homebrewで入れたPythonでのlinkエラー問題

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

少数のあれこれ【備忘】

小数の桁数を取得する

$num1 = 12.345;
strlen(substr(strrchr($num1, '.'), 1));

解説

  • strrchr関数を使用して、ドット[.]の開始位置を取得
  • substr関数を使用して、ドット[.]の次の値以降を取得
  • strlen関数を使用して、取得文字の長さを取得

参考

小数点以下の数字を指定桁数だけ丸める

  • 丸める
    • 1.23 という値を小数点第一位で返したい場合、1.2で返す
    • 1.28 という値でも同様に小数点第一位で返したい場合は、1.2で返す
    • 上記数字を小数点第二位で返したい場合、1.23 は、1.23を返し、1.28 は、1.28を返す
$digit = 3;
$fraction = 1.2345;
round($fraction + 0.5 * pow(0.1, $digit), $digit, PHP_ROUND_HALF_DOWN)

解説

  • pow関数を使用して、指数を作る
    • 末尾に0.5を足す事で、繰り上げ・下げをせず 元々の値から指定した桁数だけ切り捨てて返すようにするため
  • round関数を使用して、浮動小数点を$precisionの値だけ丸める
    • その時、第3引数に PHP_ROUND_HALF_DOWN を指定して、0に近く方向に値を丸める
    • 例えば、1.5 → 1 にして、-1.5 → -1 にする

参考

小数の整数部と小数部をそれぞれ取得する

$num1 = 12.345;

$integer = floor($num1);
$fraction = $num1 - $integer;
$digit = strlen($integer);

解説

  • floor関数を使用して、整数部分を取得
  • 元の値から、整数部分を引いて、少数部分を取得
  • strlen関数を使用して、整数部分の桁数を取得

参考

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

最長AB列を求める

最長AB列を求める

文字'A'と文字'B'からなる文字列Sがあります。
Sに含まれる連続した区間で'A'と'B'の数が等しいものの最長の長さを答えよ。

ソースコード

<?php
//入力値
$nyu = "ABBBAAAAA";
//評価する値(それぞれの区切った値を配列に格納)
$hyo1 = "A";
$hyo2 = "B";
$arrray1=explode($hyo1, $nyu);
$arrray2=explode($hyo2, $nyu);

//A、B配列の数分だけ、レングスを格納する
for($i = 0; $i < count($arrray1); $i++){
  $arr1_len[$i] = strlen($arrray1[$i]);
}
for($i = 0; $i < count($arrray2); $i++){
  $arr2_len[$i] = strlen($arrray2[$i]);
}


//取得した配列のmax値を取得
$arr1_max = max($arr1_len);
$arr2_max = max($arr2_len);
//A,B双方で取得したmax値の低いほうを取得
$arr_min = min($arr1_max,$arr2_max);

//結果値
$kekka = $arr_min * 2;
print($kekka);

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

JSで公開鍵を使って暗号化したデータをPHPで秘密鍵を使って復号する

公開鍵暗号を用いて、JSでデータを暗号化したものをPHPで復号してみます。
記載内容に問題があっても責任は取れないので、参考にする場合は自己責任でお願いします。
また、参考にした公式のドキュメントを載せるので、情報が変わっていないかそちらを確認していただければと思います。

ちなみに、パスワードは暗号化せずにハッシュ化しましょう。

公開鍵と暗号鍵を作成する

まずは暗号化に使う鍵を作成しておきます。

$ openssl genrsa -out rsa_2048_priv.pem 2048
$ openssl rsa -pubout -in rsa_2048_priv.pem -out rsa_2048_pub.pem

1024bitで作成している記事もありますが、2048bit以上のRSA鍵を使用するようにしましょう。

JSでの暗号化について

調べてみると、JSで暗号化を行う場合に様々なライブラリが存在しました。
英語ですが、下記のgistにJSで暗号化させる時のライブラリの一覧が載っています。
JavaScript Crypto Libraries

ただ、node.jsだとデフォルトでcryptoというモジュールが使えますし、多くのブラウザではSubtleCrypto オブジェクトが実装されています。

今回はLaravelのプロジェクトで使うことを想定して、node.jsのcryptoモジュールを使用します。

cryptoモジュールで暗号化する

公開鍵で暗号化するにはcrypto.publicEncryptというメソッドを使用します。

const crypto = require('crypto');

// 先ほど作成したpublickey
const publicKey = `-----BEGIN PUBLIC KEY-----
hogehogehogehgoe
-----END PUBLIC KEY-----
`;

const plain = 'hoge fuga';
const encrypted = crypto.publicEncrypt(publicKey, Buffer.from(plain));
console.log(encrypted);
console.log(encrypted.toString('base64'));

ドキュメントを見るとpublicEncryptの第二引数が buffer となっていますが、new Buffer() は廃止予定となっており、使用するとwarningが出たと思います。
いくつか記事を調べると、new Buffer(plain) を使用している記事が多いですが、Buffer.fromを使用して、バイナリデータに変換します。

参考
今年のうちに対応したい、Node.jsのBufferに潜む危険性

暗号化した結果を確認してみる

base64エンコードした結果をサーバ側へ送り、DBなどに保存をする想定です。

Base64は、データを64種類の印字可能な英数字のみを用いて、それ以外の文字を扱うことの出来ない通信環境にてマルチバイト文字やバイナリデータを扱うためのエンコード方式である

$ node publicEncrypt.js
# console.log(encrypted);
<Buffer b7 8c 41 4b 5e 3b 84 59 4a 31 f7 e5 53 a4 41 5d 55 c3 05 7d 27 18 9d 6d d6 eb 15 45 7d 53 01 99 79 eb ea d7 95 d6 2e 31 c1 b1 2c 76 ed cc 48 4a dd ea ... >
# console.log(encrypted.toString('base64'));
t4xBS147hFlKMfflU6RBXVXDBX0nGJ1t1usVRX1TAZl56+rXldYuMcGxLHbtzEhK3equw7qE8cCNulqJfst40B5OSThpkUoEiA9Q1Bo6b5RIhyZi8IQsTSZCkNE2LQaEILl5JZXfioYXmjTfMqDhn/jI6QLagWkRyd2jYIZVQ6ChhjzYG2eVWo5otli/N4Z9j93FKKQ1n8fiIvY62lZsOSv9I0F/ZZzCcYPFvI1DeOuYIw6StHC20lPo49d6quNjAAZwiLI9p43kue1PnD4M2HwwVFWYr4DRveDkR3gIUhOGk8UDI8BeCrwjVsz9jT5MjpAVp/6BT8/W1Q0NgJyctA==

PHPでの復号について

openssl-private-decryptを使うことで復号可能です。

// 先ほど作成した秘密鍵
$privatekey = <<<EOD
-----BEGIN RSA PRIVATE KEY-----
hogehogehgoe
-----END RSA PRIVATE KEY-----
EOD;

// 先ほど暗号化した文字列
$encrypted = 't4xBS147hFlKMfflU6RBXVXDBX0nGJ1t1usVRX1TAZl56+rXldYuMcGxLHbtzEhK3equw7qE8cCNulqJfst40B5OSThpkUoEiA9Q1Bo6b5RIhyZi8IQsTSZCkNE2LQaEILl5JZXfioYXmjTfMqDhn/jI6QLagWkRyd2jYIZVQ6ChhjzYG2eVWo5otli/N4Z9j93FKKQ1n8fiIvY62lZsOSv9I0F/ZZzCcYPFvI1DeOuYIw6StHC20lPo49d6quNjAAZwiLI9p43kue1PnD4M2HwwVFWYr4DRveDkR3gIUhOGk8UDI8BeCrwjVsz9jT5MjpAVp/6BT8/W1Q0NgJyctA==';
// base64エンコードしているのでデコードしてから復号する
$decoded= base64_decode($encrypted);
openssl_private_decrypt($decoded, $decrypted, $privatekey, OPENSSL_PKCS1_OAEP_PADDING);

var_dump(decrypted);

ポイントとしては、openssl_private_decrypt の第四引数にOPENSSL_PKCS1_OAEP_PADDING を指定しているところかなと思います。

crypto.publicEncryptのドキュメントに、RSA_PKCS1_OAEP_PADDING を使用していると記載がありますので、php側でも同じパディングを指定しています。

Otherwise, this function uses RSA_PKCS1_OAEP_PADDING.

下記のように第四引数に指定しない場合には、$decryptedがnullとなってしまいます。

openssl_private_decrypt($decoded, $decrypted, $privatekey);

復号した結果を確認してみる

これでできた

$ php privateDecrypt.php
string(9) "hoge fuga"

連携部分について

公開鍵も秘密鍵もサーバ側で持っておいて、公開鍵のみをcsrf-tokenと同じようにhtmlのmetaに埋め込むか、公開鍵を取得するためのAPIなどを用意してJSで取得をできるようにすれば良いと思います。

注意点

公開鍵暗号では容量の大きいデータをまとめて暗号化できない

当たり前かもしれませんが、公開鍵暗号では大きなサイズのデータを暗号化できません。
そのため、そこそこ長い文章などを暗号化しようとすると、下記のようなエラーが表示されてしまいます。

Error: error:0409A06E:rsa routines:RSA_padding_add_PKCS1_OAEP_mgf1:data too large for key size

容量の大きいデータを暗号化してサーバ側に送りたい場合には、公開鍵と共通鍵を使ったハイブリッド暗号で対応する必要があると思います。

参考
java – RSAで暗号化できるデータ量の制限は?

うまく復号できない場合に確認すること

最初復号できなかったのですが、teratailで同じような質問があり、参考にさせてもらいました。
使うライブラリやメソッドなどによって、設定に差異がないかを確認する必要ありそうです。

・暗号アルゴリズム(AES等) 
・鍵長(256ビット等) 
・モード(CBC等) 
・パディング(pcks7等) 

参考
JSとPHPでの公開鍵・秘密鍵による暗号化と復号化

その他参考情報

RSA暗号運用でやってはいけない n のこと

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

EclipseでPHPのデバッグを可能にする(xdebugを使用)

Eclipse上でPHPのデバッグを実行したいと考えたのですが、設定に結構苦戦しました。
同じ悩みを抱える人も多いようなので、備忘録として公開してみます。ほぼ素人の考え方なので、見当違いな部分もあると思いますが、そこは勘弁してください。

OS: Windows7 64bit
PHP: 7.3.7
Apache: 2.4.39

※PHPをローカル環境で使う場合は、xamppを利用することが多いと思いますが、今回の例ではPHPとApacheを単独でインストールします。

PHPとApacheのインストール・設定

PHPのインストールはこちらを参照、
Apacheのインストールはこちらを参照しました。

PHP: 7.3.7
Apache: 2.4.39

phpinfo()を使って、設定内容を確認する画面を出します。こちらを参照。下図のようにブラウザに表示されるはず。このページはこのまま表示させておきます。

xdebugの入手とphp.iniの設定

こちらのページで、xdebugのdllをダウンロードします。さっき表示しておいたPHP設定内容を確認する画面で、内容を全部コピー(ctrl + a → ctrl + c で大丈夫)し、ダウンロードページのテキストエリアに貼り付けます。これで必要なxdebugのバージョンを調べてくれるので、「Analyse my phpinfo() output」ボタンを押してダウンロードへ。

自分の環境では、"php_xdebug-2.7.2-7.3-vc15-x86_64.dll"というファイルがダウンロードされました。このdllファイルを、php.iniと同階層にある"ext"フォルダに格納します。

次にphp.iniを開き、末尾に以下のように追加。

[XDebug]
zend_extension=C:\php-7.3.7\ext\php_xdebug-2.7.2-7.3-vc15-x86_64.dll
xdebug.remote_connect_back=On
xdebug.remote_enable = 1
xdebug.remote_host = "localhost"
xdebug.remote_port = 9000

zend_extension= には、xdebugのdllを格納したディレクトリを指定。
xdebug.remote_port は、9000にしておくのが一般的みたいです。

Apacheを再起動し、再びphpinfo()でPHPの設定を確認。下図のようにxdebug関連の情報が表示されていればOK。

screenshot.3.jpg

eclipseのインストールと設定

※eclipseをインストールする前に、Apache、PHP、xdebugのダウンロードと設定、php.iniの設定をしておいたほうが無難なようです。
先にeclipseをインストールしたら、起動時にサーバー関連のエラーが出るようになってしまったので。自分の知識不足とは思いますが。
エラーの発生を止められなくなった時に、eclipseのworkspaceのフォルダを丸ごと削除したら発生しなくなりました。プロジェクト等を作成してからではできないことなので、先にデバッグができる環境を整えることを優先すべきかと思います。

こちらから、Eclipse IDE for PHP Developersをダウンロードし、インストール。
PHP開発用のプラグインもありますが、PHP専用のeclipseを別にインストールするほうが無難な気がします。動作がおかしくなっても、インストールし直せば済むことになるので。

Pleiadesで日本語化もしておきましょう。

eclipseをインストールしたら、「ウィンドウ」→「設定」→「PHP」→「インストール済みのPHP」で、新規のPHP実行可能ファイルを登録します。
screenshot.4.jpg

「次へ」ボタンで、デバッガー設定へ。「デバッガー」はXdebug、ポートは9000を指定。この設定は不要かもしれませんが、やっておいたほうがいいとは思います。
screenshot.5.jpg

プロジェクト用のディレクトリとファイル作成

通常はeclipseのworkspaceにプロジェクトのディレクトリを作成しますが、今回はApacheのドキュメントルートの下に作成します。
screenshot.7.jpg

Apacheをインストールしたディレクトリの、"htdocs"フォルダの下にプロジェクト用のディレクトリを作成。
eclipseから、このディレクトリをインポートします。

screenshot.6.jpg

構成としては、下図のようになっています。
screenshot.8.jpg

「実行」→「デバッグ構成」から、PHP Webアプリケーションで新規構成を選択。
最初に、PHPサーバーを新規登録します。
screenshot.9.jpg
サーバー名は適当に。ベースURLはlocalhost指定で。ドキュメントルートは、Apacheのドキュメントルートを指定。
「次へ」でデバッグの設定へ。
screenshot.10.jpg
デバッガーはXdebug。ポートは9000を指定。「完了」ボタンで終了。

screenshot.11.jpg
次にデバッグ構成を登録します。PHPサーバーは、先程新規登録したサーバーを指定。ファイルは、プロジェクトのindex.phpでいいでしょう。他のファイルを指定もできます。

これでデバッグモードで実行できるようになりました。下図のように、ブレークポイントで止まります。変数の値も取得できます。
screenshot.12.jpg

注意点として、デバッグで実行ではなく通常の実行でスタートしても、強制的にデバッグモードになってしまうようです。通常の実行をするなら、「実行の構成」でデバッグを"None"にしたPHPサーバーを指定しておくといいようです。
screenshot.13.jpg

全般的に、こちらのページが非常に参考になりました。このページを見たほうが分かりやすい人も多いと思います。

今回参考にしたリンク。
PHP入門
Apache入門
phpinfo関数による設定内容の確認
Xdebug Tailored Installation Instructions
Eclipse Packages Download
Pleiades Plugin ダウンロード
【PHPへ xdebug導入】は簡単。実はEclipseで動かす設定が環境依存で若干ややっこい
Eclipse PHP(Pleiades All in One)でハマった件 起動でエラー

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

入力値は全体の何パーセント

入力した数値から全体の何パーセントなのかを求めるソース

ソースコード

<?php
// Your code here!
//全体値を入力
$Population = 1000;
//求めたい人口を入力(0だと未入力)
$Cut = "0";

//割合計算用の数値に変換
$wariai = $Cut * 0.1;

//割合を求める
if(!$Cut){
    echo "入力値が入っていません";
} else {
    $kekka = $wariai / $Population * 100;
    echo "入力した値は、全体値の$kekka";
    echo "%です";
}

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

PHPでRARファイルの解凍をする

はじめに

RARファイルの解凍をやりたかったけど、公式ではZipArchiveしかないみたい。
PECLの方にRarArchiveというのがあるみたいなので、これを使う。

環境

  • Windows 7
  • PHP 7.1

RarArchiveのダウンロードと設定

ここからダウンロード。
僕はWinなのでVer.4.0.0 の「DLL」 -> 「7.1 Non Thread Safe (NTS) x86」をダウンロードしました。
ダウンロードしたやつの中身に「php_rar.dll」があるので、それを php の ext フォルダに配置。
php.iniに以下追記。

php.ini
[PHP]
extension=php_rar.dll

追記後、サーバー再起動。

解凍

$archive = RarArchive::open("dir/");
$entries = $archive->getEntries();
foreach ($entries as $entry) {
    $entry->extract($to_dir);
}
$archive->close();

ZipArchive との違いは、一度getEntriesしないと中身がとれない。

ついでに、ファイル名が日本語だと Failed to open になるっぽいです。

さいごに

公式で扱える形式増やして(懇願

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

PHPでObserverパターンで実装してみた

はじめに

Rx****をやってみたくなり、基本となる(?)ObserverパターンをPHPにて実装して見た。

前提条件として、

  • PHP5.xでも動く
  • ある程度実用的

とします。

Observerパターンとは

あちこちのページで書かれているので細かくは省略します。

状態変化に応じた処理を記述するときに有効らしいです。

今回のサンプル

タイトルと内容を入力とするフォームからPOSTされた時に、

  • メール送信
  • ログ出力

を行う処理を記述するのにObserverパターンで記述してみます。

(この程度でObserverパターンを使うのもって話はあるが。。。(汗)

クラス図はこんな感じ

class.png

ソース構成はこんな感じ

┣━ classes

┃  ┣━ ifSubject.php

┃  ┣━ clsFormdat.php

┃  ┣━ ifObserver.php

┃  ┣━ clsLogObserver.php

┃  ┣━ clsSndMailObserver.php

┗━ smp1.php

実装

インターフェイス

まずSubjectのインターフェイスは

ifSubject.php
<?php
/**
 * Subjectインターフェイス
 */
interface ifSubject {
  public function addObserver($obj);      // Observerインスタンス追加
  public function removeObserver($obj);   // Observerインスタンス削除
  public function notify();               // Observerへの通知
}

次にObserverインターフェイスは

ifObserver.php
<?php
/**
 * Observerインターフェイス
 */
interface ifObserver {
  public function execute($obj);   // Subjectからの通知受信
}

Subjectクラス

clsFormdat.php
<?php
require_once(dirname(__FILE__)."/ifSubject.php");

/**
 * フォームデータクラス
 */
class clsFormdat implements ifSubject {
  private $title = ""; /// タイトル
  private $memo = "";  /// 内容

  private $arrObservers = NULL;  /// Observerインスタンス配列

  public function __construct() {
    $this->arrObservers = array();
  }

  /**
   * タイトルと内容のセッター
   */
  public function __set($itm,$val) {
    if ( $itm == "title" ) $this->title = $val;
    if ( $itm == "memo" ) $this->memo = $val;
  }

  /**
   * タイトルと内容のゲッター
   */
  public function __get($itm) {
    if ( $itm == "title" ) return $this->title;
    if ( $itm == "memo" ) return $this->memo;
  }

  /**
   * Observerインスタンス追加
   * @param $obj Observerインスタンス
   */
  public function addObserver($obj) {
    $this->arrObservers[get_class($obj)] = $obj;
  }

  /**
   * Observerインスタンス削除
   * @param $obj Observerインスタンス
   */
  public function removeObserver($obj) {
    unset($this->arrObservers[get_class($obj)]);
  }

  /**
   * Observerへの通知
   */
  public function notify() {
    foreach($this->arrObservers as $obj) {
      $obj->execute($this);
    }
  }
}

Observerクラス

まず、ログ出力クラスは

clsLogObserver.php
<?php
require_once(dirname(__FILE__)."/ifObserver.php");

/**
 * ログ出力クラス
 */
class clsLogObserver implements ifObserver {
  public function __construct() {

  }

  /**
   * 通知受信
   * @param $obj clsFormdatのインスタンス
   */
  public function execute($obj) {
    $buf  = "title:".$obj->title;
    $buf .= "\n".$obj->memo;
    $fp = fopen("/var/www/Log/app.log","a");
    fwrite($fp,$buf);
    fclose($fp);
  }
}

次にメール送信クラス

clsSndMailObserver.php
<?php
require_once(dirname(__FILE__)."/ifObserver.php");

/**
 * メール送信クラス
 */
class clsSndMailObserver implements ifObserver {
  public function __construct() {

  }

  /**
   * 通知受信
   * @param $obj clsFormdatのインスタンス
   */
  public function execute($obj) {
    mail("tst@cde.jp",$obj->title,$obj->memo,"From: adm@cde.jp");
  }
}

動かしてみる

次のコードで実際に動かしてみる。

smp1.php
<?php
require_once(dirname(__FILE__)."/classes/clsLogObserver.php");
require_once(dirname(__FILE__)."/classes/clsSndMailObserver.php");
require_once(dirname(__FILE__)."/classes/clsFormdat.php");

$obj = new clsFormdat();

$objLog = new clsLogObserver();
$obj->addObserver($objLog);

$objMail = new clsSndMailObserver();
$obj->addObserver($objMail);

$obj->title = "サンプルタイトル";
$obj->memo  = "サンプルサンプル\nサンプル";
$obj->notify();

確かに実装はシンプルになったようだ。

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

Laravel vs Symfony(DDDの場合)

PHP で DDD(ドメイン駆動設計)をする案件でウェブフレームワークの選定を行いました。

フレームワーク選定にあたり、以下の方針を立てました。

  • 実績のあるフレームワークから選定(Laravel か Symfony の2択としました)
  • DDD が提唱している不変条件をモデルに実装出来ること
  • フレームワークと戦わない(目的達成の為に Hack 等の小細工をしない)
  • 人がレビュー等で注意するといった回避策で妥協しない
  • なるべくシンプルにフレームワークを使う

本来、フレームワークはドメイン層に関心がなく DDD に制約を与えないはずです。
一方で DDD はドメイン層以外には具体的な実装方法を提示していません。
DDD をやるのにどのフレームワークを使ってもいいのでは?と思うのですが、実際にはそうではありませんでした。

フレームワークが採用している ORM によって DDD しやすいものと困難なものがあります。

FW ORM パターン 調査結果
Laravel Eloquant Active Record 設定が無く簡単
モデルで constructor や public, private といったアクセス修飾子が自由に使えない
モデルに不変条件の実装ができずDDDに不向き
Symfony Doctrine Data Mapper DBのテーブルカラムとモデル属性のマッピング設定が必要
モデルで constructor や public, private といったアクセス修飾子が自由に使える
モデルに不変条件の実装ができてDDDに向いている

上記表の結果から PHP での DDD 案件では Symfony を採用することに決定しました。

我々は Laravel をとても気に入っていたので、なかなか Laravel をあきらめる気にはなれませんでした。しかし、色々と調べたのですが良い方法は見つかりませんでした。Active Record でもうまくDDDやっているという方がいましたら、ぜひ、知見をお聞かせください。

参考情報

モデルでドメイン知識を表現するとは何か・・・不変条件について、とても分かりやすい記事です

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

Laravelでのマルチ認証でログイン後のリダイレクト先をroleで変更する方法

概要

セッション衝突問題とかは置いておいてシンプルにroleを使ってログイン先を変更する話です。
Laravelデフォルトの認証を使います。

UsersテーブルにRoleを追加

migrationファイルでroleを追加しておきまましょう。
roleは数値でもenumでも良いです。
今回は admin,teacher,parentを用意したと仮定して進めます。

redirectTo()

Auth\LoginControllerにredirectToメソッドを定義してあげます。

そうすると既に定義してあるprotected $redirectToではなくredirectTo()の戻り地でredirect先が決定するようになります。

この辺りのことは\Illuminate\Foundation\Auth\RedirectsUsers.phpに書いてあります。

trait RedirectsUsers
{
    /**
     * Get the post register / login redirect path.
     *
     * @return string
     */
    public function redirectPath()
    {
        if (method_exists($this, 'redirectTo')) {
            return $this->redirectTo();
        }

        return property_exists($this, 'redirectTo') ? $this->redirectTo : '/home';
    }
}

traitがtrait先のコントローラーでメソッドかプロパティの定義を期待しているのはちょっと変わった実装ですね。

LoginControllerにredirectTo()を実装

public function redirectTo(){
    $role = $this->guard()->user()->role;
    if($role === 'admin'){
      return '/admin';
    }
    if($role === 'teacher'){
      return '/teacher';
    }
    if($role === 'parent'){
      return '/parent';
    }
    return '/';
}

roleに当てはまらなかったら例外投げても良いのですが今回は'/'にします。

まとめ

これでログイン後のリダイレクト先を変更することが出来ました。
role別に認証や認可でアクセス先を制限するのは別な実装が必要です。

参考

Laravelで複数テーブルを使ったログイン認証を実装する
https://qiita.com/sakuraya/items/248d1bed5857c69e44c4
【Laravel】Laravel5.5でユーザーと管理者の認証(MultiAuth)を実装する(マルチログイン実装時のセッション分割対応)
https://qiita.com/sola-msr/items/65634826bcedf3ea4ca4
やはりお前らのMulti-Auth は間違っている
https://qiita.com/h1na/items/bbe4d17af21860de1930

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

ssh(sftp)サーバーをLaravelのストレージとして利用する

はじめに

Laravelのファイルストレージで外部のサーバーのファイルを取り扱いたい場面に遭遇して解決方法が見つかったのでメモとして残します。

ファイルストレージは標準ではローカル ディスクとAmazon S3、Rackspace、ftp用のアダプタが組み込まれています。調べてみるとFlysystemというライブラリの機能で実現されていて、アダプタを追加すれば様々なデバイスをストレージとして利用できる仕組みになっています。

Laravelには入っていませんがFlysystemには標準でsftpのアダプタが用意されています。これを利用すると任意のsshサーバーをsftp経由でLaravelのストレージとして利用可能です。

手順

  1. composer で sftp アダプタのモジュールをインストール

    composer require league/flysystem-sftp
    
  2. サービスプロバイダでアダプタを追加する。

    <?php
    
    namespace App\Providers;
    
    use Storage;
    use League\Flysystem\Filesystem;
    use League\Flysystem\Sftp\SftpAdapter;
    use Illuminate\Support\ServiceProvider;
    
    class AppServiceProvider extends ServiceProvider
    {
        ...
        public function boot()
        {
            /**
              * flysystem に sftp 用のアダプタを追加する
              * @see https://flysystem.thephpleague.com/docs/adapter/sftp/
              * @see https://readouble.com/laravel/5.5/ja/filesystem.html#custom-filesystems
              */
            Storage::extend('sftp', function ($app, $config) {
                return new Filesystem(new SftpAdapter($config));
            });
        }
    }
    
  3. config/filesystems.php にディスクを追加する。

    <?php
    
    return [
        ...
         /**
          * sftp アダプタで ssh サーバーを disk として登録する
          *
          * @see https://flysystem.thephpleague.com/docs/adapter/sftp/
          */
        'rpx' => [
            'driver' => 'sftp',
            'host' => '192.168.10.11',
            'port' => 22,
            'username' => 'vagrant',
            // 'password' => 'vagrant',
            'privateKey' => '/home/vagrant/.ssh/id_rsa',
            'root' => 'rpx',
            'timeout' => 10,
        ],
    ];
    

    接続先サーバーには対応する公開鍵を登録する。

  4. ストレージで利用する

    Storage::disk('rpx')->put('./a/test.txt', "hello rpx\n");
    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[PHP][GraphQL]13月32日を受け入れないようにする、とある1つのScalar

ちょっと試してみたところから、正しいってどこかな?と予想外に探したという小話です

環境

  • PHP 7.3
  • Laravel 5.8
  • Composer 1.8.6
  • nuwave/lighthouse 3.6.1
  • mll-lab/laravel-graphql-playground 1.0.2

こんなことが

年月日時分秒の時刻を示す文字列から、その「月末」を出す関数を実行した結果です

custom-scalar-0.png (74.5 kB)

わざとあり得ない日時を渡したのですが、思わぬ方向に処理された、という感じです

正しくしようとして

ここでの「正しい」は、「間違ったものとして、処理させない」とします

カスタムScalarをつくる

公式1でも「実装次第」とかいているので、実装していきます

# php artisan lighthouse:scalar DateTime
Scalar created successfully.

# php artisan lighthouse:scalar Date
Scalar created successfully.

実装していったのがこちらになります
- https://github.com/sogaoh/LaravelPractice/blob/master/server/app/GraphQL/Scalars/DateTime.php
- https://github.com/sogaoh/LaravelPractice/blob/master/server/app/GraphQL/Scalars/Date.php

(そして、元から用意されてたのは使わないようにコメントアウト)
schema_graphql.png (30.6 kB)

実装のポイント

何をもってこの年月日時分秒の時刻を示す文字列を「正しい」とするかを判定するメソッドを、自分はこのようにしました

    private function validate($target): bool
    {
        if (!\is_string($target)) {
            return false;
        }
        return $target === \date('Y-m-d H:i:s', \strtotime($target));
    }
  • 文字列じゃなかったらダメ
  • PHP標準の date 関数・strtotime 関数 で時刻オブジェクト化して文字列フォーマットにした結果が、
    元の年月日時分秒の時刻を示す文字列と完全一致してなかったらダメ

ここがミソ(と思っているところ)

  • 標準関数のみで実装
    • 便利な Carbon クラスを敢えて使っていない
  • 例外処理を利用することを避けている
    • 必要以上のことを積んで重くするレイヤーじゃないと思って

結果

custom-scalar-1x.png (80.1 kB)

間違ってるよ、とはじくようになりました :sunglasses:

一方で、正しいものを意図通りに結果を出すのもOK

custom-scalar-2o.png (72.0 kB)

まとめ(というか自分の結論)

  • ありがたく用意されているものでも、期待されている結果が得られるかを確かめたうえで使うのが良さそう
  • 何が「正しい」のかを考えるのは、とても良い頭の体操になる
  • 便利な高水準のオブジェクトを利用するシーン、プリミティブな比較的低水準の標準関数などを利用するシーンを適切に使い分けることができると楽しい

関連情報

そして

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