スキップしてメイン コンテンツに移動

投稿

PHPで呼び出されたメソッド階層を取得

デバッグ目的で、メソッドの呼び出された階層を取得したいときがあると思います。 PHPでは、Exceptionを生成してException::getTraceAsStringメソッドでStack Traceを取得する方法が簡単です。 下記にコードを示します。 <?php function extractTrace($endLineNumber, $startLineNumber=1) { $stackTrace = (new \Exception())->getTraceAsString(); $start = strpos($stackTrace, "#".$startLineNumber); $end = strpos($stackTrace, "\n#".($endLineNumber+1)); if($start !== false || $end !== false) { return substr($stackTrace, $start, ($end - $start + 1) ?: strlen($stackTrace)); } return $stackTrace; } 簡単な解説です。 引数でStack Traceを取得する範囲を指定できるようにしています。 \Exceptionを生成した個所からStack Traceが生成されるので、Stack Traceの1行目の"#0"の部分を取り除くためにデフォルトでは、$startLineNumberを1に設定しています。 $stackTrace = (new \Exception())->getTraceAsString();の部分を外部から引数で渡すのもありですが、毎回同じ処理を書く必要があるので、extractTrace関数内部に入れてあります。

PDOで指定したクラスにデータを割り当てて取得する方法

PDOで指定したクラスにデータを割り当てて取得するには、\PDO::FETCH_CLASSをMyPdo::fetch, MyPdo::fetchAllメソッドの引数に指定すれば簡単に実現できます。 通常はPDO::FETCH_ASSOCを指定して、array形式でデータを取得する方が手軽ですが、classでデータを扱うと、下記のメリットがあります。 arrayよりもclassでデータ取得した方がメモリ使用量が少ない arrayよりも、どんなデータを扱っているのかが明確になる それでは、\PDO::FETCH_CLASS使用例を下記に示します。 <?php $sql = <<<EOF SELECT id, name, weight, price FROM table_prodict EOF; $conn = new MyPdo(....); $conn->prepare($sql); $stmt = $conn->execute(); // クラスは第2引数で指定 $stmt->fetchAll(\PDO::FETCH_CLASS, Product::class); Porductクラスは下記を想定しています。 <?php class Product { public $id; public $name; public $weight; public $price; } \PDO::FETCH_CLASSの挙動についての補足です。 クラスを特に指定しないと、取得したカラムに対応したプロパティを持ったstdClassのインスタンスで結果が返ってきます。 指定したクラスに、クエリから取得したカラムに対応したプロパティがない場合(例えば、上記のProductカラムに$priceプロパティがない場合)は、動的にプロパティが定義されて取得したデータがセットされます。 \PDO::FETCH_PROPS_LATEを使うとプロパティにデータをセットする前に、クラスのコンストラクタが呼ばれます。クラスの事前処理が必要な場合に指定すると便利です。 クラスのプロパティ定義がprivateでも正しくデータはセットされます😲

PHPでarrayをgroup byする関数

通常はデータベース上でSQLを使ってgroup byをすれば十分ですが、下記のような場合プログラム側で実施するのもありです。 DBから取得したデータから別の複数の集計(group by)結果を得ることができる DBのgroup byの実行に時間がかかり、何度も似たような集計をDBに計算させるのは実行コスト(時間、CPU負荷)が高い 今回は汎用的にPHP側でgroup byを実行できるコードを書いてみました。 任意の複数フィールドで集計できるようにするため、多少コードが複雑になっています。 またkeyのencode/decode部分の処理で多少無駄があります。 <?php function groupBy(array $rows, array $groupByFields, callable $aggregate) { $groupByFieldsAsKey = array_flip($groupByFields); $map = []; foreach($rows as $row) { // キーになるならどんな関数でもOK。ここではjson_encodeを採用 $key = json_encode(array_intersect_key($row, $groupByFieldsAsKey)); $aggregatedRow = &$map[$key] ?? []; $aggregate($aggregatedRow, $row); } $result = []; foreach ($map as $key => &$aggregatedRow) { // キーをデシリアライズして、フィールドと値を集計結果の行にコピー $keyValue = json_decode($key, true); foreach($groupByFields as $groupByField) { $aggregatedRow[$groupByField] = $keyValue[$groupBy

MySQLのON DUPLICATE KEY UPDATEで複数行に対して、キーが重複しない行は挿入、キーが重複する行は新しいデータで更新する方法

MySQLのON DUPLICATE KEY UPDATEで複数行に対して、キーが存在する行は挿入、キーが重複する行は新しいデータで更新するクエリは、下記のように、VALUESキーワードを使うことでシンプルに書くことができます。 INSERT INTO table(id, score) VALUES (1, 87), (2, 75) ON DUPLICATE KEY UPDATE score = VALUES(score);

Json形式の文字列をPHP形式の配列表示に変換するプログラム

json形式の文字列をPHPの配列形式で、そのまま貼り付けて利用するためのちょっとしたコードです。 とりあえず開発中に楽をするためのプログラムなので、厳密さよりも簡便さ重視で書きました。 json形式の文字列をPHPの配列に変換 PHPの配列をJSON_PRETTY_PRINTで整形してjson形式の文字列に再変換 jsonの括弧やコロンをPHPの配列の形式に合うように変換 <?php echo strtr(json_encode(json_decode($jsonString), JSON_PRETTY_PRINT), [':' => '=>', '{' => '[', '}' => ']']);

PHPのXMLReaderを使ったXMLの読み込み

はじめに PHPでXMLを読み込むには、通常はSimpleXMLElementを使えば十分です。 ただし、XMLが巨大でメモリを節約して処理する必要がある場合は、XMLのパーサーであるXMLReaderを使って処理する方法があります。 XMLReaderを使って読み込む際はXMLの構造をどうとらえるかによって、プログラムの書き方が変わるのと、毎回読み込みの方法をプログラムしなければならないのが欠点です。 今回は下記のサンプルXMLのproduct部分を読み込むXMLReaderのプログラムのサンプルを、XMLReaderの機能紹介も兼ねて、いくつか示します。 <?xml version="1.0" encoding="UTF-8"?> <products> <date type="1">20200414</date> <product> <maker>AMD</maker> <name>Ryzen 3400G</name> </product> <product> <maker>Intel</maker> <name>Core i9 9900K</name> </product> </products> サンプルプログラムでは、下記のPHPの配列形式を取得することを目標にします。 実際の用途ではXMLReaderを使ってXMLを読み込むと同時に、CSVファイルに出力するなどといった処理が考えられます。 この場合、メモリはXMLReader部分と、読み込み途中で一時的に保持しているデータのみで利用されるので、メモリの使用量は最低限に抑えることができます。 [ [ "maker"=> "AMD", "name"=> "Ryzen

PHPで与えられた配列の次元数を取得する方法

PHPの多次元配列で次元数を推定する関数の紹介です。 [注意] 配列の最初の要素だけをチェックしていくので、すべての配列の次元が同じであること前提としています。 そもそも配列の次元数がバラバラ(C#でいうjagged array)であれば、今回紹介する関数は使えません。 <?php function getDimension(array $source) { if(is_array($source)) { return getDimension(reset($source)) + 1; } else { return 0; } } // 下記のような配列を引数で与えます。 $source[1]['A']['a'] = true; $source[2]['B']['a'] = true; $source[3]['A']['a'] = true; $source[4]['B']['a'] = true; // 結果は3になります。 getDimension($source);