- 投稿日:2019-05-07T21:17:07+09:00
RedHatがサポートしてるphpのバージョンが分かりづらかったけど分かったのでメモ
- 最新のphpサポートはここに書いてあるけど、RedHatは最新のphpをサポートしてるわけではない
- RedHatの公式サイトにある通り、RHEL7はphp5.4系をサポートし続けるし、RHEL6はphp5.3をサポートし続ける
- 本家phpがサポート終わっても脆弱性が出た際などにREHLはパッチを提供し続ける
- 安定稼働のため
- RHEL7でサポートされたphp7などを使いたい場合は、Red Hat Software Collections Productを利用すると良いが、デフォルトでバンドルされてるバージョンと比べるとサポート期間が短い
- 投稿日:2019-05-07T20:07:49+09:00
演算子.と+の優先順位が変わる
すなわち左から右に評価されます。
echo 1 . 2 + 3 . 4; echo ((( 1 . 2 ) + 3 ) . 4 ) ; // これと同じマニュアルでもわざわざ例を挙げて解説しています。
さて2019年3月にChange the precedence of the concatenation operatorというRFCが提出されました
2019/05/07現在は投票中のステータスですが賛成多数で、PHP8で上記の動作は変わることになりそうです。
演算子の追加削除はよくあることですが、優先順位の変更というのは他言語含めてもなかなか見ることのないレアなイベントではないでしょうか。Change the precedence of the concatenation operator
Introduction
+、-、そして.は長年にわたる問題です。
それは左から右に解釈されます。echo "sum: " . $a + $b; // こう解釈される echo ("sum: " . $a) + $b; // こうではない echo "sum :" . ($a + $b);このRFCでは、この動作をより直感的に、問題が出にくくなるようにすることを目的としています。
Proposal
現在、
+、-、.の各演算子は優先順位が同じです。
これらは、単純に左から右に評価されます。これは直感に反します。
一般に、数字ではない文字列を足したり引いたりすることはほとんどありません。
PHPが整数を文字列にシームレスに変換できることを考えると、文字列連結が先に来ることが望ましいでしょう。従って、このRFCでは
.に、+・-より低い優先順位を与えることを提案します。
具体的には、新しい優先順位は<<・>>のすぐ下になります。Backward Incompatible Changes
.の後に、括弧を使わず+、-を使用している式全てが影響を受けます。
例として、"3" . "5" + 7は42ではなく"312"になります。これは、予告や警告なしに出力が変更されるという点で微妙な動作変更ですが、コードを静的解析して、この問題が発生する箇所を全て見つけることは簡単にできます。
私の知るかぎり、この問題が発生することは稀であり、そして大抵は最初から間違っています。NikitaがMLで言及したように、既存のOSSへの影響は事実上ありません。
見つかったものは全て単なるバグです。
つまり、全体的に影響は非常に小さいということになります。Proposed PHP Version
PHP7.4でE_DEPRECATEDを発生させ、PHP8で動作を変更します。
投票
投票開始は2019/04/30、投票終了は2019/05/14です。
2019/05/07現在、PHP8で動作変更する提案は賛成23反対3で、ほぼ確実に受理されます。
PHP7.4でDeprecatedにする提案は賛成23反対4で、こちらもおそらく受理されます。NikitaのML
またNikitaか。
Composerパッケージの上位2000件を調査したところ、影響を受けるコードは僅か5件しかありませんでした。
しかも5件とも修正後の優先順位を想定したコードで、つまり現状ではバグっています。$this->errors->add( 'unknown_upgrade_error_' . $errors_count + 1, $string );例を一つあげると、上記コードは
add('unknown_upgrade_error_5', $string)のような文字列を与えたいのだと思われますが、実際はadd(1, $string)になります。今回のRFCが通ると、想定していたであろう動作にエンバグすることになります。
感想
考えてみたら、むしろどうして今まで同じだったんだ、って感じですね。
優先順位が同じであるという仕様と、文字列と数値を自由に行き来できるという仕様が相まって、PHPで.と+を同時に使った演算は、ぱっと見から予想できない結果になることがありました。
今回のRFCが通ると、そのあたりの動作がすっきりすることになります。もっとも、そういったややこしい演算には普通は括弧を使っているから、実害は全くないはずですけどね。
ところでDeprecateにする提案に"The second (secondary) voting requires a 50%+1 majority."って書いてあるんだけど、50%の投票は廃棄されたんじゃなかったのか?
- 投稿日:2019-05-07T19:29:24+09:00
wp-adminでWP APIをJSから扱う
wp-adminからWP APIをJSからコールする方法を試したので覚書。
WP APIをコールする際の注意点
認証が必要なAPIは、
X-WP-Nonceをヘッダーで送る必要がある。wp-api.jsのimport
nonceの生成などをよしなにしてくれるのでimportしておく。
ついでにPOSTリクエストにjqueryを使うのでこちらもいれておく。add_action('wp_enqueue_scripts', 'add_scripts' ); add_action('admin_enqueue_scripts', 'add_scripts' ); function add_scripts() { if ( is_user_logged_in() ) { wp_enqueue_script( 'jquery' ); wp_enqueue_script( 'wp-api' ); wp_enqueue_script( "custom_js", plugins_url( 'libs/scripts.js', __FILE__ ), array( 'jquery', 'wp-api' ) ); } }libs/scripts.js
リクエスト処理部分。
wp-api.jsを入れていると、window.wpApiSettingsという値がwp-adminでは定義されている様子。
これにWP APIのパスとnonceが入っているので使う。const settings = window.wpApiSettings const requestObj = { url: settings.root + 'wp/v2/posts', method: 'POST', beforeSend: function ( xhr ) { xhr.setRequestHeader( 'X-WP-Nonce', settings.nonce ); }, body: { /* post params */} } jQuery.ajax( requestObj ) .then( result => console.log( result ) )wp-api.jsをちゃんと使えばjQueryもいらない気はする。
ただ、Backbone.js触ったことがないのでそれはまた時間ができたときにでも。
- 投稿日:2019-05-07T19:23:48+09:00
[PHP]IN句のプレースホルダーを配列から作成する
- 投稿日:2019-05-07T17:26:10+09:00
ストアドプロシージャを jQuery + PHPで呼び出してみた
はじめに
・トランザクション処理をストアドプロシージャにまとめることでソースをすっきりさせたい衝動にかられ、ストアドプロシージャを呼び出してみた。
・いままでクライアント側にSQLをベタ打ちしていてセキュリティーの問題がやばいみたいなので...開発環境
・サーバー構成
WindowsServr2012R2
Apache + SQLServer2012 + PHP
・クライアント構成
JavaScript + jQuery実装コード
・クライアントからのAPIの呼び出し
classDatabase.js//========================================= // Database クラス // データベースに関する命令をまとめる //========================================= const DatabaseAsyncOn = true; const DatabaseAsyncOff = false; function Database(accountName, userName, async) { this.accountName = "dev"; // accountName; this.userName = "dev"; // userName; this.async = false; // async; } Database.prototype = { //========================================= // SELECT メソッド(prepareを使用したもの) // DBからSQLを使ってデータをGETする //========================================= getProcedures: function(mappingName, prepare) { var returnData = null; $.ajax({ type: "POST", url: "../../common/php/getProcedures.php", data: { "accountName": this.accountName, "userName": this.userName, "mappingName": mappingName, "prepare[]": prepare }, dataType: "json", async: this.async, // 成功したときの処理 success: function(data) { returnData = data; console.log('\n\nmappingName: ' + mappingName); console.log('parametter: ' + prepare); console.log('data: ' + data); }, // エラー処理 error: function(data) { returnData = false; console.log('\n\nmappingName: ' + mappingName); console.log('parametter: ' + prepare); console.log('data: ' + data); } }); return returnData; } };main.js// APIの呼び出し // マッピング:200402 self.midokuMember = self.database.getProcedures( 'mapping200402',[ self.companyID, eventNo, today ] );サーバー側
ストアドプロシージャ呼び出し用のAPIsetProcedures.php<?php $accountName = $_POST["accountName"]; $userName = $_POST["userName"]; $mappingName = $_POST["mappingName"]; $prepare = $_POST["prepare"]; $org_timezone = date_default_timezone_get(); date_default_timezone_set('Asia/Tokyo'); $dsn = 'sqlsrv:server=【IPアドレス】;database=' . $accountName; $password = '【DB接続パスワード】'; try { $dbh = new PDO($dsn, $userName, $password); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE , PDO::FETCH_ASSOC); ////////////////////////////////////////////// // セット処理 ////////////////////////////////////////////// // ステートメント準備 $stringPrepare = "{CALL ".$mappingName."("; foreach ($prepare as $key => $value) { # code... $stringPrepare = $stringPrepare."?,"; } if (count($prepare) != 0) { $stringPrepare = substr($stringPrepare, 0, -1); // 最後尾の文字を削除する } $stringPrepare = $stringPrepare.")}"; error_log($stringPrepare); $stmt = $dbh->prepare($stringPrepare); for ($i=0; $i < count($prepare); $i++) { # code... $stmt->bindParam($i+1, $prepare[$i], PDO::PARAM_STR); } $stmt -> execute(); echo "\nPDO::errorInfo():\n"; print_r($dbh->errorInfo()); $dbh = null; date_default_timezone_set($org_timezone); } catch (PDOException $e) { echo 'Error : ', $e->getMessage(), "\n"; die('MSSQL Serer Connect Error'); } ?>呼び出し元のストアドプロシージャ
mapping200409.sqlUSE [dev] GO /****** Object: StoredProcedure [dbo].[mapping200402] Script Date: 2018/12/26 12:10:09 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER PROCEDURE [dbo].[mapping200402] @companyID nvarchar(4), @eventNo int, @today nvarchar(10) AS BEGIN TRY BEGIN TRANSACTION --トランザクションの開始 BEGIN -- マッピング2:未読者の取得 SELECT MK_smartPhone.companyID, MK_smartPhone.staffID, MK_staff.name FROM MK_smartPhone INNER JOIN MK_staff ON MK_smartPhone.companyID = MK_staff.companyID AND MK_smartPhone.staffID = MK_staff.staffID LEFT OUTER JOIN MK_history ON MK_smartPhone.companyID = MK_history.companyID AND MK_smartPhone.staffID = dbo.MK_history.staffID WHERE (MK_smartPhone.name <> N'card') AND ((MK_history.category = 2) AND (MK_history.flag = 0) AND (MK_history.companyID = @companyID) AND (MK_history.externNo = @eventNo) AND (MK_staff.retireDate > @today OR MK_staff.retireDate IS NULL OR MK_staff.retireDate = '') OR (MK_history.flag IS NULL)) GROUP BY MK_smartPhone.companyID, MK_smartPhone.staffID, MK_staff.name, MK_history.externNo ORDER BY MK_smartPhone.staffID END COMMIT TRANSACTION --トランザクションを確定 END TRY --例外処理 BEGIN CATCH ROLLBACK TRANSACTION --トランザクションを取り消し PRINT ERROR_MESSAGE() --エラー内容を戻す PRINT 'ROLLBACK TRANSACTION' END CATCH RETURNまとめ
・SQLを別ファイルに分離してソースの読みやすさ管理のしやすさの向上が図れた。
・言語ごとに得意分野があるのでそれぞれ適材適所で使用していきたい。※違う言語が紛れているとソースが読みづらくなる下記の様な使い分けがよいと改めて思った。
①JavaScript・・・画面周り、APIの呼び出し(ビジネスロジックは書かない)
②PHP・・・ビジネスロジックだけを書く
③ストアドプロシージャ・・・トランザクションの処理(ビジネスロジックは書かない)
- 投稿日:2019-05-07T16:10:36+09:00
PHP おみくじメモ
rand()を知った時におみくじ作れるんじゃね?と思って作ってみた。(当たり前ですが、すでにあるものは無視して自分で考えて作ることを意識してます。)
ただ、それだけ。入力画面
index.php
1から9までの数字を入れてGOボタンを押してね! <form method="post" action="result.php"> <input type="text" name="fig"> <input type="submit" value="GO"> </form>結果を表示する
result.php
$fig = $_POST['fig']; $result = rand(1,9); if ($fig == $result) { echo "当たりだよ。おめでとう!"; } else { echo "外れだよ。ごめんね!"; } echo '<br />';一応動作はしたので、メモ程度に載せておきます。
余談
実務で学ばないとどのようなイレギュラーがあるかわからないし、対処法もわからないのでこんなイレギュラーがあるよって人いたらコメントください。
それについて勉強しますので。
- 投稿日:2019-05-07T13:53:39+09:00
php-master-changes 2019-05-06
今日は FFI のバグ修正、テストの修正、不要テストの削除、ドキュメントの修正、JIT の比較処理の生成コードの修正があった!
2019-05-06
cmb69: Properly initialize variable
- https://github.com/php/php-src/commit/30409d6753efc4da50d4a7b1180ff1c442735781
- [7.4~]
- ext/ffi で、変数の初期化を追加
bukka: Fix OpenSSL online test for ca context
- https://github.com/php/php-src/commit/9977de00284c2b702e4aadfcc66069befa0aaf75
- [7.2~]
- ext/openssl で、http://www.php.net を向いていたテストを(https リダイレクトされるので) http://www.nginx.org に変更
- 「いや example.com でよいのでは」的なツッコミが入ってる
peter279k: Remove duplicate getimagesize_variation_003 test
- https://github.com/php/php-src/commit/8417142a8fc0a3ccca5c19ab3c8dbde05a737b92
- [7.4~]
- 重複している不要なテストを削除
petk: Remove duplicate test file
- https://github.com/php/php-src/commit/340c39acfa165990150b68ac52d11dbdc2003eba
- [7.4~]
- ext/oci8 で、重複している不要なテストを削除
petk: Remove phpextdist
- https://github.com/php/php-src/commit/e58fddf7a3d23b5b7b3cda48c5062ea4c274d5c0
- [7.4~]
- 使われていないスクリプト phpextdist の削除
petk: [ci skip] Move wddx to dedicated section
- https://github.com/php/php-src/commit/6be6cac6f4c41338c57642e9f6747f7f7711d1d3
- [7.4~]
- UPGRADING で、wddx についての記述を "Other Changes to Extensions" から "Removed Extensions and SAPIs" の節へ移動
petk: Rename uppercase JPG file to lowercase jpg
- https://github.com/php/php-src/commit/c025cf47c00ed5db086c69ed76dc88ca4238255e
- [7.4~]
- ext/exif で、テスト用 jpg ファイル名の拡張子が大文字だった箇所の修正
petk: Fix double incremenation in ffi
- https://github.com/php/php-src/commit/426bcdf02120604cc13d9af9f161aec4307a2249
- [7.4~]
- ext/ffi で、誤ったループカウンタ操作の修正
dstogov: Improved code for unordered comparison
- https://github.com/php/php-src/commit/66a1d821d3092db9cf8923b91361aca66d12f791
- ext/opcache/jit で、比較処理の生成コードを修正
- 投稿日:2019-05-07T13:24:25+09:00
Laravel のテストであるクラスのプロパティをテストから書き換えて初期化するには
この記事について
半分個人メモです。
ある特殊なテストを行いたくて、private なプロパティをテストメソッドから書き換えて実行する方法を探っていたところ、以下のやり方を思いついたので書き残しておきます。
はじめに
概要
あるクラスが config から読み取った値を private なプロパティにセットして初期化していて、その値を、あるテストメソッドでのみ、特殊な環境変数で上書きして実行したい、というケースに遭遇した際、リフレクションと無名クラスで対応できそうだったのでやってみました。
環境
- PHP: 7.3.2
- Laravel: 5.8.13
- PHPUnit: 7.5.8
詳細
とあるクラス
<?php namespace App\Services; class SomeService { private $config; public function __construct() { $this->config = config('services.some.config'); } public function doSomething() { return $this->config; } }テストクラス
<?php namespace Tests\Feature\Services; use App\Services\SomeService; use Tests\TestCase; class SomeServiceTest extends TestCase { public function testDoSomethingInGeneralEnvironment() { $service = app(SomeService::class); $this->assertEquals('hoge', $service->doSomething()); } public function testDoSomethingInSpecialEnvironment() { $service = new class extends SomeService { public function __construct() { $class = new \ReflectionClass(SomeService::class); $property = $class->getProperty('config'); $property->setAccessible(true); $property->setValue($this, env('SOME_SERVICE_CONFIG_OVERWRITTEN')); } }; $this->assertEquals('fuga', $service->doSomething()); } }config/services.php
'some' => [ 'config' => env('SOME_SERVICE_CONFIG'), ],.env
SOME_SERVICE_CONFIG=hoge SOME_SERVICE_CONFIG_OVERWRITTEN=fuga
SOME_SERVICE_CONFIG_OVERWRITTENは、.env.testing とか phpunit.xml とかに書いてもいいかもしれないです(テストでしか使わないやつなので)。
- 投稿日:2019-05-07T13:24:19+09:00
Windows環境のVSCodeでxdebugでremote_connect_back=1でリモートデバッグを行いたいときの設定(プロキシ環境)
概要
ドはまりしてしまったVSCodeのxdebugの設定について記載します。
VSCodeをつかって社内LANでつながっている開発サーバにxdebugをセットアップし、リモートデバッグをしようとしました。サーバ側の設定(php.ini)やクライアント側の設定(VSCode)の設定は割愛します。
(ページ下部の参考にしてほしいサイトを確認してください)ホストを設定してからのリモートでバッグ
xdebug.remote_host=172.20.1.30上記であればうまくいきましたが、
xdebug.remote_connect_back=1これだとだめでした。デバッグがとまりませんでした。なぜ。
結論
結論から言うと、プロキシのせいでした。
プロキシを通って開発サーバにアクセスという経路を通っていたため、remote_connect_backで許可する範囲を超えてしまっており、リモートデバッグができていませんでした。プロキシを通さずに開発サーバにつなげる設定箇所は下記のとおりです。
(もちろん環境によってはできないこともあります。可能かどうかはネットワークに詳しい人に構成を確認してもらいましょう。だめならremote_hostで設定すればいいだけの話です。めんどくさいですし忘れやすいところですが。)
ここにチェックをする、もしくは
ここに例外のホスト記述を行います。
(「172.20.*」など)原因がプロキシでなさそうな方へ
下記を確認してみてください。参考にしてほしいサイトです。とても参考になります。
[PHP] Xdebug のリモートデバッグ、理解していますか?
https://qiita.com/castaneai/items/d5fdf577a348012ed8af【PHP】リモートマシンのデバッグを、「複数人で」やる【Xdebug×DBGpProxy】
https://qiita.com/takudo/items/35d2fd3c6e9846715bf4
- 投稿日:2019-05-07T10:05:07+09:00
Laravel5のRequestクラス覚え書き
Webアプリケーションフレームワーク Laravel の
Illuminate\Http\Requestの書き方メモ。リファレンス的なもの。Controllernamespace App\Http\Controllers; use Illuminate\Http\Request; class UserController extends Controller { public function store(Request $request) { $name = $request->input('name'); // } }View@extends('layouts.app') @section('body') <form method="POST" action="{{ route('XXXX') }}"> @csrf <input type="text" name="name" value=""> // </form> @stop入力の取得
動的プロパティで入力を取得
$name = $request->name;全入力を連想配列で取得
$input = $request->all();アップロードファイルを除いて、全入力を連想配列で取得
$input = $request->input();すべてのクエリストリングを連想配列で取得(上記のallやinputにもクエリストリングは含まれる)
$input = $request->query();アップロードファイルの取得
$file = $request->file('csv');"name" の入力を取得
$name = $request->input('name');デフォルト値を指定して取得
$request->input('name', 'Tom');"name" と "password" のみ取得
$input = $request->only('name', 'password');"password" 以外を取得
$input = $request->except('password');リクエストに値が存在するか
$request->has('name')すべて存在するか
$request->has(['name', 'email'])リクエストに値が存在し、かつ空でないか
$request->filled('name')フラッシュデータ(次のリクエストの間だけ利用できるデータ)の保存
すべてをフラッシュデータに保存
$request->flash();"name" と "email" のみフラッシュデータに保存
$request->flashOnly(['name', 'email']);"password" を除いてフラッシュデータに保存
$request->flashExcept('password');セッション
"key" をセッションから取得
$request->session()->get('key');デフォルト値を指定してセッションから取得
$request->session()->get('key', 'default');その他
リクエストURIを返す
$uri = $request->path();HTTPメソッド名(GET、POST等)を返す
$method = $request->method();POSTメソッドか
$request->isMethod('POST')リクエストURIがパターンに合致するか
$request->is('admin/*')User-Agentを返す
$request->header('User-Agent');
- 投稿日:2019-05-07T03:36:43+09:00
LaravelでLeague/Csvを使ってアップロードされたcsvを読み込みDBに保存する
環境
Laravel 5.6
League/Csv 9.2前提
- Qiita初投稿です、お手柔らかにお願いします。
- 今回はバリデーションについては実装、言及しません。
導入
League/Csvをインストールする。
composer dump-autoloadは多分必要。composer require league/csv composer dump-autoload今回ぶち込みたいCSV
company_name name hoge株式会社 佐藤 exapmle1@example.com foo株式会社 山田 exapmle2@example.com 実装
UserController.php<?php namespace App\Http\Controllers; use App\Eloquent\User; use Illuminate\Http\Request; use League\Csv\Reader; use League\Csv\Statement; class UserController extends Controller { // 省略 public function importCSV(Request $request, Statement $stmt, User $user) { $file_path = $request->file('file')->getPathname(); // ReaderはDIできないらしい。 $csv = Reader::createFromPath($file_path, 'r')->setHeaderOffset(0); $records = $stmt->process($csv); $data = []; // 後ほど解説。 foreach ($records as $record) { $record['created_at'] = now(); $record['updated_at'] = now(); $data[] = $record; } $user->insert($data); return redirect()->route('user.index'); } }解説
ここでリクエストされたfileの一時保管パス?的なのを取得する。
$file_path = $request->file('file')->getPathname();そしてここ、
foreach ($records as $record) { $record['created_at'] = now(); $record['updated_at'] = now(); $data[] = $record; } $user->insert($data);$recordsには
ResultSet {#375 ▼ #records: LimitIterator {#372 ▶} #header: array:4 [▶] }というオブジェクトが入っているのでforeachで取得していくとなんと、各recordには
[ "company_name" => "hoge株式会社" "name" => "佐藤", "email" => "example1@example.com" ]などの配列が入っているというわけ。
Laravelだとinsertの引数に各レコードを値にもつ多次元配列を渡すことで複数レコードまとめて挿入できる。
しかし、insertは「created_at」「updated_at」が更新されないので、foreachで回しながら入れている。おまけ
今回のroute
web.phpRoute::post('/users/csv', "UserController@importCSV")->name('user.importCSV');今回のview
users.blade.php{{ Form::open(['url' => route('user.importCSV'), 'method' => 'POST', 'class' => '', 'files' => true]) }} <div class='form-group'> <input type="file" name="file" value=""> </div> <button type="submit">csv読み込み</button> {{ Form::close() }}応用編
大切なことはすべて公式ドキュメントが教えてくれた。
9.0のドキュメント↓
https://github.com/thephpleague/csv/blob/master/docs/9.0/index.md<?php use League\Csv\Reader; use League\Csv\Statement; $csv = Reader::createFromPath('/path/to/your/csv/file.csv', 'r'); // headerは何行目か。 $csv->setHeaderOffset(0); $header = $csv->getHeader(); //return [0 => "企業" // 1 => "名前" // 2 => "メールアドレス"] // 10行飛ばして25行とる $stmt = (new Statement()) ->offset(10) ->limit(25); $stmt->process($csv);まとめ
2019年でPHPでcsvをいじりたいってなると、goodbyか、このLeague/Csvの二強な感じがしていて、今回書きやすそうだしGitHubのスターもダントツでLeague/Csvの方が高かったのでこちらを採用しました。
今回は使わなかったのですが、goodbyは省メモリを売りにしているそうなのでメモリを気にする量のcsvを裁くときは視野に入れたいですね。
結局、Laravelが好きです。引用
https://github.com/thephpleague/csv/blob/master/docs/9.0/index.md




