20191004のPHPに関する記事は10件です。

Mysql:AutoIncrementIdを推測するワザ

概要

バイナリサーチでAIDを推測するだけ

ありがちな遅いクエリ

SELECT *
FROM `sugoi_table`
WHERE `create_time` >= '2019-10-01'
(48,126 合計, クエリの実行時間 28.3798 秒)

Explainの結果

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE sugoi_table range PRIMARY PRIMARY 4 NULL 32715643 Using where

rows(走査範囲)が多すぎる!!

  • MySQLは「1番目のレコードから全てのレコードのcreate_timeが10/1より大きいか?」を判断するので大変遅い
  • 本番write用のDBにこんなもん流した日にはしばらくサービスが反応しなくなる
  • せめて迂闊なselectを投げる前にindexが効いてるか確認しよう・・・

シンプルな解決方法

AIDで走査範囲を絞る

SELECT *
FROM `sugoi_table`
WHERE `user_id` >=50000000
AND `create_time` >= '2019-10-01'
(48,153 合計, クエリの実行時間 0.0621 秒)

早くなった

Explainの結果

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE sugoi_table range PRIMARY PRIMARY 4 NULL 500844 Using where

rowsが3200万→50万に減ったのが要因

  • 適当に作った調査クエリを流して「結果が返ってこない・・・」とかなりそうな時は事前に使おう

だがしかし、それっぽいAIDを調べるのが面倒くさい!!

crate_timeで二分探索(バイナリサーチ)すればいい

アルゴリズムを勉強するなら二分探索から始めよう! 『なっとく!アルゴリズム』より

文字が読みたくない人向け

  • 全体の中心のデータを見て「大きい」なら後ろ、「小さい」なら前を繰り返して検索する方法
  • 計算量はO(log2 n)だそうな

実装してみる

/**
 * 指定テーブルの指定日付のAIDを推測する
 */
public static function guessAid( $tableName, $targetKey, $targetDateTime) {

    //まず最大のAIDを取得する
    $maxId = self::_getMaxAid($tableName, $targetKey);

    $low    = 1;
    $high   = $maxId;
    $loopCnt = 0;
    $guess  = 0;

    while ( $low <= $high ) {
        $loopCnt++;
        $mid = (int)(($high + $low) / 2); //中央値を作成 floatにならないように
        $sql = "SELECT create_time FROM {$tableName} WHERE {$targetKey} >= {$mid} LIMIT 1";
        $tmpData = Db::query($sql);
        if ( empty($tmpData)) {
            //データが無い時の処理(投げやり);
            break;
        }
        if (strtotime($tmpData['create_time']) < strtotime($targetDateTime)) {
            //時間が小さい時に推測候補を保持しておく
            //逆条件の時にループが終わると指定時間ちょい後のAIDになってしまう場合がある為
            $guess = $low; 
            $low = $mid + 1;
        } else {
            $high = $mid - 1;
        }
        //データが消されている可能性も考慮して一定ループで抜ける
        if ( $loopCnt >= 50 ) {
            break;
        }
    }
    //echo("だいたいこの辺じゃろ");
    $id = $guess;
    return $id;
}

/**
 * テーブルのMAX_AIDをとる
 */
private static function _getMaxAid( $tableName="", $targetKey="") {
    $sql = "SELECT MAX({$targetKey}) FROM {$tableName}";
    $data = Db::query($sql);
    return $data;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CakePHP3の嵌りポイント

CakePHPは簡単かつウェブサイトが楽に作れる大変ありがたいフレームワークですが、たまに嵌りポイントがあります。
使っていてハマった点を忘れないように纏めます。


No.1 - patchEntity, save等で孫テーブルがうまく動かない(マージ/保存できない)

親-子-孫の関係になっているテーブルに対し、hasOne/hasManyも定義しているのに親テーブル追加/更新で孫まで上手く反映されない。子まではうまく動く、get/findもうまく動く、エラーは出ない。という場合。
例: Companies --(hasMany)--> Users --(hasMany)--> UserSettings

可能性① associationの書き方が間違ってる

get()とは違い、ネストした中にもassociatedを再度書く必要がある

#get()の場合
$query = $companies->get($id, [
    'contain' => [ 'Users' => ['UserSettings'] ]
]);

#newEntity/patchEntityの場合
$company = $companies->newEntity($data, [
    'associated' => [
        'Users' => [
            'associated' => ['UserSettings']
            // ↑ここにも'associated'が必要
        ]
     ]
]);

公式マニュアルにも書いてあるものの、まさかget/findと違うことはないだろと思って読み飛ばしがち
*参考にさせてもらったページ(https://qiita.com/zaramme/items/719f77480c5f0cde0ae1)

可能性② Entityの$_accessibleに子/孫要素が入ってない

この指定がtrueになっていないと無視される。
bin bakeで自動で設定してくれるはずなものの、条件によっては?親側のEntityに書いてくれていない事があるようで、bakeしなおすか手動で追加が必要。
(普通にget/findしてアクセスするときと同様、hasOneなら単数形、hasManyなら複数形で子テーブルの名前を追加)

可能性③ dirtyフラグが立ってない

newEntityやpatchEntityでエンティティ作成後、手動で何か追加/変更した場合、dirty()も呼ばないと更新対象にならない。(公式)

$company->author->name = 'Master Chef';
$company->dirty('author', true);

No.2 - 更新処理で子/孫テーブルのデータが作り直される(delete&insertされる)

例: Users --(hasMany)--> UserSettings
親テーブル更新時に子テーブルのデータもまとめて更新したいが、親はupdate処理されるものの、子テーブルのデータがdeleteで一回消され、insertで新規保存されてしまう場合。
updateでないため子テーブルのprimary keyが変わってしまい、他との関係性が壊れる。

可能性: 子/孫テーブルのprimary keyが渡されてない

Entityにprimary keyが無い場合は入れ直し、有る場合はupdate処理になる。


No.3 - 単数/複数形の規約で混乱する

単数/複数の変形

規約で使い分けた気持ちは分かるけど不規則の場合はどうする?
実際のCakePHPの変換を出してくれるサイトを参照。(古い?)
最新Verで動いているcakePHP公式のサイトもあり。
pokemonは単複同型です。

DBからcontainで纏めて取ってきた子/孫テーブルの形は?

例: Companies --(hasMany)--> Users --(hasOne)--> Addressies

hasOne, belongsToは単数形、そのまま要素にアクセスできる。
$user->address->street ←addressと単数形になる

hasMany, belongsToManyは複数形で配列。
$company->users[0]->name

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

Ajaxの説明とJQueryによる簡易実装

初めに

前の座席の同僚に「お前、Ajaxって知ってる?」と言われたことが悲しくて執筆に至ります。
本記事はAjaxに関する簡単な説明と処理の実装を目的としています。

Ajaxとは

Ajaxとは、「Asynchronous JavaScript + XML」の略です。
直訳すると、「非同期のJavaScriptとXML」となります。
そもそも非同期とは何のことでしょうか。

同期通信と非同期通信

まず初めにブラウザとサーバのHTTP通信の基本的な流れについて説明します。

  1. ブラウザ:サーバに対してリクエストを送信
    例:Google検索窓に「小松菜奈」と入力してクリック
  2. サーバ:ブラウザからのリクエストを処理
    例:「小松菜奈」に関するデータを抽出し、良い具合に加工
  3. サーバ:2の処理完了後、ブラウザにレスポンスを送信
    例:加工済み「小松菜奈」データを送信
  4. ブラウザはレスポンスに基づき画面を描画す 例:「小松菜奈」に関する検索結果が表示

以上のように、ブラウザとサーバ間ではリクエストとレスポンスのやり取りがされます。

1~4を一連の流れとしたとき、
4まで完了しないと1の操作が不可能なものが同期処理
4まで完了していなくても(任意のタイミングで)1の操作が可能なものが非同期処理
です。

より簡単に言うと、
・同期通信:通信が完了するまで次の処理を行うことができない通信
・非同期通信:通信の途中でも他の処理を行うことができる通信
となります。

XML

ところで「Asynchronous JavaScript + XML」のXMLとは一体何のことでしょうか。
XMLは、「Extensible Markup Language」の略で、文章の意味や内容、構造をタグを用いて整理するマークアップ言語の一つです。他の代表的なマークアップ言語にはHTMLがあります。
Webページの見た目を整えるHTMLに対し、XMLは、機械に対して情報を伝達するのに長けていると言われています。
また、W3C(World Wide Web Consortium)によるオープンな規格であることから、多くのシステムに対する汎用性もあり、注目度の高い言語となっています。

ここまでのまとめ

Ajaxとは、
1. JavaScriptを用いて、
2. XML形式のデータを、
3. 非同期に通信する
技術だと理解しておけばよいでしょう。

Ajaxの実装例

環境

  • macOS High Sierra 10.13.6
  • JQuery 3.4.1
  • PHP 7.1.16

それではAjaxを用いた簡単な処理を実装していきます。
まずはHTMLファイルから。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <!-- 今回はJQueryを用いてAjax通信を実現するため、GoogleのCDN経由でJQueryを読み込む -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
  <title>世界一美しいのは誰か</title>
</head>
<body>
  <form method="post">
    <p>世界で一番美しい人物の名前を答えなさい</p>
    <p>あなたの答え:<input type="text" name="answer" id="answer"></p>
  </form>

  <button id="ajax">Ajax通信</button>
  <div class="result"></div>

  <script>
    $(function(){
      // 「Ajax通信」ボタンをクリックしたら発動
      $('#ajax').on('click',function(){
        $.ajax({
          url:'./nana.php',
          type:'POST',
          data:{
            'answer':$('#answer').val()
          }
        })
        // Ajax通信が成功したら発動
        .done( (data) => {
          $('.result').html(data);
        })
        // Ajax通信が失敗したら発動
        .fail( (jqXHR, textStatus, errorThrown) => {
          alert('Ajax通信に失敗しました。');
          console.log("jqXHR          : " + jqXHR.status); // HTTPステータスを表示
          console.log("textStatus     : " + textStatus);    // タイムアウト、パースエラーなどのエラー情報を表示
          console.log("errorThrown    : " + errorThrown.message); // 例外情報を表示
        })
        // Ajax通信が成功・失敗のどちらでも発動
        .always( (data) => {
          if($('#answer').val() == '小松菜奈'){
            console.log('あなたは正しい');
          }else{
            console.log('あなたは間違っている');
          }
        });
      });
    });
  </script>
</body>
</html>

ソースコードのコメントをお読みいただくと理解が深まるかと思います。
console.logはよくデバッグに用いられるので、開発中にどの処理を通過しているのかを探るときに便利です。

次にPHPファイルです。

nana.php
<?php
header('Content-type: text/plain; charset= UTF-8');
if(!empty($_POST['answer'])){
    $answer = $_POST['answer'];
    if($answer == '小松菜奈'){
        $result = $answer."は世界一美しい";
    }else{
        $result = $answer."は世界一美しいわけではない";
    }
    echo $result;
}else{
    echo '文字を入力してください';
}
?>

中身の処理はシンプルで、Ajaxで送信されたinput要素の値を$_POST['answer']で取得し、その値によって返すデータを変えています。

全体の流れ

ローカルサーバとしてPHPのビルトインサーバを用いました。
PHPがインストールされていれば、php -S localhost:ポート番号で立てることができます。
以下、通信が成功した例を示します。

1. ~/index.htmlをブラウザで表示すると以下のようになる。
スクリーンショット 2019-10-04 18.59.36.png

2. フォームに文字を入力し「Ajax通信」ボタンを押下。
ここで、入力した値がブラウザからサーバへ送信される。
スクリーンショット 2019-10-04 19.08.22.png

3. 送信された値がnana.phpファイル内で処理され、加工された値が返却される。
※このnana.phpファイルはサーバ内に存在。

4. 返却された値をブラウザが受け取り、.doneの処理を実行し、描画。
スクリーンショット 2019-10-04 19.24.18.png

スクリーンショット 2019-10-04 19.27.05.png

また、上図のようにコンソールに出力がある。これは、通信成功と失敗に関わらず以下の処理が実行されたためである。
スクリーンショット 2019-10-04 19.48.14.png

上記の1~4の手順において、ページの切り替えが生じないこともAjaxの利便性としてよく挙げられる。

終わりに

浅学ながらフロント開発には欠かせないAjaxの基本について執筆させていただきました。
誤りがありましたらコメントにてご指摘いただけると幸いです。

参考文献

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

PHP5で16進数を符号付10進数に変換する方法

経緯

仕事でPHPで0xから始まる16進数を符号付きの10進数に変換する必要があったが、ネットで探してもなかなか見つからなかったため備忘録として記載。

結論

    //16進数を符号付き10進数に変換する
    private function hex_to_decimal($val){
        //0x表記の場合
        if(strpos($val,"0x") === 0){
            //「0x」の箇所を削除する
            $hex = substr($val,2);
        }
        //先頭文字の1ビット目が1→マイナス
        if(hexdec(substr($hex,0,1)) & 8 && strlen($hex) === 8){
            //2の補数で絶対値を取得
            $hex = (hexdec($hex) ^ (16 ** strlen($hex)-1))+1;
            //マイナスに変換
            $dec = 0 - $hex;
        } else {
            //先頭文字の1ビット目が0→プラス
            $dec = hexdec($hex);
        }
        return $dec;
    }

解説

符号付16進数はビット列の最初の値で正負の判断をしている。
・先頭ビットが1→負
・先頭ビットが0→正
例として負の値-1(16進数:0xFFFFFFFF)で解説。

まずは、下記の箇所

    if(hexdec(substr($hex,0,1)) & 8){

hexdec(substr($hex,0,1))で16進数の最初の文字「F」の10進数(=15)を取得。
15(2進数で1111)と8(2進数で1000)のビット積を求めることで、最初のビットが1か0かを判定することができる。(&はビット積の意)
最初のビットが1の場合は負の値のため、下記の処理に移る。

    $hex = (hexdec($hex) ^ (16 ** strlen($hex)-1))+1;

ここでは元の16進数(0xFFFFFFFF)と32ビット分全て"1"の数値を排他的論理和でビット演算を行い、その数値にプラス1をすることで2の補数で負の値の絶対値を取得している。

その後に、下記の計算で負の値に変換して終了。

    $dec = 0 - $hex;

最後に

もっといい方法あったら教えてください!

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

(めも)PHPで文字数制限をかけて出力する。

echo substr('mojiretsu' , 始点 ,終点);

例:文字列がmojiretsuの場合に2文字目から4文字目までを出力する。
$str = 'mojiretsu'
echo substr($str ,1 , 4);

oji

mb_substr()

*substr()との違い。

substr():バイト数でカウント。半角1文字で1バイト、全角2文字で2バイト。
mb_substr():mbはマルチバイトの意。文字数でカウント、半角でも全角でも1文字。

なので日本語の文字列を使うならmb_substr()の方が楽。

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

strlen()の挙動について

strlen()

事件

本番稼働中のシステムの改修をしてると
以下の記述がありました。
(変数名は実際のものから変更しております。)

if (isset($data['content']) && strlen($data['content'] > 0)) {
    // うんたらかんたら
}
strlen($data['content'] > 0)

えっなにこれ
こわい

なにがこわいって本番稼働してるうえに今のところバグとされる報告もないし、
テストしても問題なかったんですよね。

挙動を試す

値がある場合

$array = array('content' => 'こんてんと', 'title' => 'たいとる');
echo '正:';
echo strlen($array ['content']) > 0 ? 'true' : 'false';
echo '<br>誤:';
echo strlen($array ['content'] > 0) ? 'true' : 'false';

結果

正:true
誤:true 

ふぇーっ

値がある場合(数値)

$array = array('content' => '10', 'title' => '0');
echo '正:';
echo strlen($array ['content']) > 0 ? 'true' : 'false';
echo '<br>誤:';
echo strlen($array ['content'] > 0) ? 'true' : 'false';

結果

正:true
誤:true 

うーん。。。

値がない場合

$array = array('content' => '', 'title' => 'たいとる');
echo '正:';
echo strlen($array ['content']) > 0 ? 'true' : 'false';
echo '<br>誤:';
echo strlen($array ['content'] > 0) ? 'true' : 'false';

結果

正:false
誤:false 

えっいいのこの書き方で。。。?

値が0

$array = array('content' => 0, 'title' => 10);
echo '正:';
echo strlen($array ['content']) > 0 ? 'true' : 'false';
echo '<br>誤:';
echo strlen($array ['content'] > 0) ? 'true' : 'false';

結果

正:true
誤:false 

まあそうなりますわな。

結論

括弧の閉じる位置には気をつけましょう。
今回はUI上数値ではなく文字列が入るか入らないかという作りをしていたのでたまたまよかったようです。

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

webarenaでubuntu その54

概要

webarenaでubuntu18.04やってみた。
練習問題やってみた。

練習問題

いいねボタンを設置せよ。

写真

image.png

サンプルコード

<?php
    header("Access-Control-Allow-Origin: *");
    header("Content-Type:  application/json");
    $id = "test";
    $db = new PDO("mysql:host=localhost;dbname=mydb;", 'user', 'pass');
    if (!$db)
    {
        print "connect error!! <br>";
    }
    else
    {
        $sql = "SELECT * FROM counter WHERE id='$id'";
        $rs = $db->query($sql);
        if (!$rs)
        {
            print "Error in database!! 0 <br>";
            print $sql;
        }
        else
        {
            $userData = array();
            while ($row = $rs->fetch())
            {
                $userData[] = array('count' => $row['cnt']);
            }
            echo json_encode($userData);
        }
    }
?>

成果物

http://embed.plnkr.co/PKwLyPiQP2gHurD0i0pn/

以上。

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

Laravelを使っていたら、EC-CUBE4系とも仲良くなれた

はじめに

先日、松戸市内でLaravelドキュメントを読む勉強会を開催しました。この時の開催レポートです。当日は松戸駅近くのイベントスペースFANCLUBをお借りしましたー。ありがとうございました。
会場

趣旨がちょっと変わったぞ・・・?

Laravelのドキュメントを原文で読むことでLaravelへの理解を深めること英語力を向上させることが目的の一石二鳥イベント。ただ、それだけではなくドキュメントを読んでいくうちに、段々と設計思想の話にもなり、非常に学びの多い会となりました。
特に、私が所属しているJoolenには、EC-CUBEのスペシャリストがおり、その方との会話の中での気づきが大きかったのでこちらを中心に書き残しておきます。

今回、話題にできたテーマは以下の通りです。1

  1. installation
    1. Server Requirements(サーバの要件)
    2. Installing Laravel(インストール方法)
    3. Configuration(設定方法)
  2. Web Server Configuration
    1. Directory Configuration(ディレクトリ設定)
    2. pretty urls(index.phpは隠そうよ、というお話)
  3. Directory Structure(ディレクトリ構成)
  4. Routing(ルーティング設定)

共通点

.envを使っていること

データベースへの接続文字列などは.envファイルに持たせることができます。
本番環境では、.envファイルではなく環境変数から設定を取得できるので、セキュリティの観点から、その様にしましょうという話で盛り上がりました。
もちろん、APP_ENVの指定で、参照する .envファイルを切り替えることができることも同じです。EC-CUBE4系以降でも使えるテクニックです。
切替え方法が参考になる記事

composerを使っていること

共にcomposerを使うことができるので双方ともにパッケージのインストールなどで悩むことはあまりなさそうです。ただ、composer create-projectで雛形を作れることに、EC-CUBE経験者は驚いていました:smile:(パッケージをインストールする以外にも機能があったんだ!)

DI(Dependency Injection)が使える

EC-CUBE4系もLaravelもDIを使うことができます。ただし、Laravelはコンストラクタだけではなくメソッドでもインジェクションをすることができます。テストがしやすくて良いですね:thumbsup:

相違点

まぁ、全く異なるフレームワークなので相違点ばかりなのは当たり前ですが。。。

ルーティング方法

EC-CUBE4系では、Controllerのアノテーションでルーティングや返すテンプレートを定義します。
EC-CUBEのController(抜粋)

    /**
     * 会員登録画面.
     *
     * @Route("/entry", name="entry")
     * @Template("Entry/index.twig")
     */
    public function index(Request $request)
    {
     ...
    }

一方、Laravelではroutes配下のweb.phpなど、Routingは独立しています。

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
})->middleware('auth');

コレは双方にとって、少し新鮮だった様です。ちなみに、Laravelのルーティングファイルは分割することができます。(質問された)

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    /*
     * This namespace is applied to your controller routes.
     *
     * In addition, it is set as the URL generator's root namespace.
     *
     * @var string
     */
    protected $namespace = 'App\Http\Controllers';

    /*
     * Define your route model bindings, pattern filters, etc.
     *
     * @return void
     */
    public function boot()
    {
        //

        parent::boot();
    }

    /**
     * Define the routes for the application.
     *
     * @return void
     */
    public function map()
    {
        $this->mapApiRoutes();

        $this->mapWebRoutes();

        //
    }

    /*
     * Define the "web" routes for the application.
     *
     * These routes all receive session state, CSRF protection, etc.
     *
     * @return void
     */
    protected function mapWebRoutes()
    {
        Route::middleware('web')
             ->namespace($this->namespace)
             ->group(base_path('routes/web.php'));
    }

    /*
     * Define the "api" routes for the application.
     *
     * These routes are typically stateless.
     *
     * @return void
     */
    protected function mapApiRoutes()
    {
        Route::prefix('api')
             ->middleware('api')
             ->namespace($this->namespace)
             ->group(base_path('routes/api.php'));
    }
}

ディレクトリ定義の自由さ

よく言われることですが、Modelsディレクトリが無いことに驚かれました。ただ、一方でこれはLaravelを利用する技術者が自らのベストプラクティスを適用できるという意味にもなります。逆にイケてない設計をすると、あとあと苦労するという噂もありますが。。。
EC-CUBEではいわゆる、リポジトリパターンをきっちり採用していますのでLaravel側でも同じ様な設計をすることで、双方の人材交流はやりやすくなるかなー、と思いました。
Laravelでリポジトリパターン
まぁ、基本的にはその企業やチームの文化やスキルセットに合わせた設計で良いか、というオチでしたが。。。

まとめ

EC-CUBE経験者とLaravel経験者で意見交換をすることで、思いも寄らない気づきをたくさん得ることができました。参加してくださった方々、本当にありがとうございました。


  1. pretty urls以降は、時間の都合でざっと眺めた程度になっちゃいました:sweat_smile: 

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

webarenaでubuntu その53

概要

webarenaでubuntu18.04やってみた。
練習問題やってみた。

練習問題

画像アクセスカウンターを表示、せよ。

サンプルコード

<?php
    $id = "test";
    $db = new PDO("mysql:host=localhost;dbname=mydb;", 'user', 'pass');
    if (!$db)
    {
        print "connect error!! <br>";
    }
    else
    {
        $sql = "UPDATE counter SET cnt = cnt + 1 WHERE id='$id'";
        $db->query($sql);
        $sql = "SELECT * FROM counter WHERE id='$id'";
        $rs = $db->query($sql);
        if (!$rs)
        {
            print "Error in database!! 0 <br>";
            print $sql;
        }
        else
        {
            $count = 0;
            while ($row = $rs->fetch())
            {
                $count = $row['cnt'];
            }
            header('Content-Type: image/gif');
            $im = imagecreatetruecolor(200, 50);
            $color = imagecolorallocate($im, 255, 255, 255);
            imagestring($im, 5, 10, 10, $count, $color);
            imagegif($im);
            imagedestroy($im);
        }
    }
?>

成果物

https://embed.plnkr.co/PITdnKLapRfpgIy4juzc/

以上。

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

PSR-12 への対応状況

PHP-FIG というPHPフレームワークなどの開発者のグループが公開している PHP Standards Recommendations (PSR) の
PSR-12: Extended Coding Style が 2019/08/10(土) に承認されました。
これにより2012年からあった PSR-2: Coding Style Guide は非推奨となっています。

PSR はPHP公式で策定されているものではないので、PSR-12 や PSR-2 は必ず守らなければならないコーディング規約というものではありませんが、有名どころのフレームワーク・パッケージで採用されており自身のプロジェクトで採用するメリットはあると思います。
参考: PSRの誤解 - Qiita

PSR-12 を採用するためのツールなどの対応状況をまとめます。
(随時追記予定)

IDE

名前 対応バージョン リンク
PhpStorm - PhpStorm 2019.3 Early Access Program is Now Open - PhpStorm Blog

Code Quality

名前 対応バージョン リンク
PHP Coding Standards Fixer - PSR-12 Support · Issue #4502 · FriendsOfPHP/PHP-CS-Fixer
PHP_CodeSniffer 3.5.0
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む