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

投稿

ラベル(PHP)が付いた投稿を表示しています

Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.

composer install コマンドを実行したところ下記の警告メッセージが表示されました。 Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled. PHPのcurl extensionが有効でないため、Composerが通常より遅くなると言われているので、PHPのcurl extensionをインストールします。 実際、curl extensionをインストールしないと遅くなりました。 Linuxのディストリビューションに応じて下記のコマンドを実行して、PHPのcurl extensionをインストールします。 apt install php-curl yum install php-curl Windowsの場合は、php.iniを編集してcurl extensionを有効化します。 php.iniの場所は php --ini をPoweerShellやコマンドプロンプトで実行すればわかります。 php.ini内の Loaded Configuration File: 付近で下記の行のコメントを削除して、有効化します。 extension=php_curl.dll

Symfony 5.xで不要なバンドルを削除する方法

Symfony 5.xで不要なバンドルを削除は下記の手順で実施できます。 config/bundles.phpからバンドルを削除 config/packageディレクトリ内の関連yamlファイルを削除(Kernel.php内で読み込まれている) composer remove vendor/package を実行。 ちなみにバンドルを削除ではなく、ロードしないようにする(無効化する)には、 config/bundles.php内でバンドルを読み込まないようにfalseに設定。 XXXBandle::class => ['all' => false], config/packages/内のyamlを削除 すればOKです。

Windows上でcomposer updateコマンドを実行した場合NoSslExceptionエラー

Windows上でcomposer updateを実行して、 [Composer\Exception\NoSslException] The openssl extension is required for SSL/TLS protection but is not available. If you can not enable the openssl extension, you can disable this error, at your own risk, by setting the 'disable-tls' option to true. というエラーが出た場合、下記の手順に従ってPHPのopenssl extesnionを有効にする必要があります。 PHPがインストールされたフォルダ内のphp.iniファイルを開きます。 extension=php_openssl.dll の含まれている行を探す 行の先頭にあるセミコロン(;)を削除してコメントイン

PHPのエラーレベル定数からエラー文字列を返すコード

PHPで定義されているエラー定数(int値)からエラー定数の文字列を返すコードです。 PHPのライブラリで定義されている情報だけで、キーが「エラー定数のint値 」値が「エラー文字列」となる連想配列を生成しています。 // 定義済みの定数を取得 $allConstants = get_defined_constants ( true ) ; // "Core"カテゴリに入っていて"E_"から始まる定数のみ取得 $errorConstants = array_filter ( $allConstants [ "Core" ] , fn ( $key ) = > strncmp ( $key , "E_" , 2 ) === 0 , ARRAY_FILTER_USE_KEY ) ; // エラー定数のint値 -> エラー文字列になるように連想配列を逆転 $errorCodeToString = array_flip ( $errorConstants ) ; // 使い方の例 echo $errorCodeToString [ E_COMPILE_ERROR ] ;

PHPのregister_shutdown_functionで登録した関数内でmemory_limitを増加させる

PHPで、特にメモリ不足のエラーが発生してプログラムが強制終了したときに、register_shutdown_functionに終了時に実行したい関数を登録することで、プログラム終了時にその登録した関数を実行することができます。 が、その登録した関数内の処理でメモリ不足になり、実行したかった処理が実行されないという問題に遭遇しました。 色々テストしたところ、register_shutdown_functionに登録した関数内でPHPのmemory_limitを増加させれば対応できることがわかりました。 といわけで現在のmemory_limitに任意のメモリ値を足すメソッドを作成してみました。 下記のsetAddedMemoryLimitByMegaByteメソッドは、引数にmemory_limitの増分を渡すことで、memory_limitの設定値を増加させることができます。 注意点として、作成したプログラム内のmemory_limitの許容フォーマットは こちら に書かれている情報に基づいて決めました。 今のプログラムは、256、128K、1024M、3Gなどは正しく処理可能です。 メモリサイズの設定値なので、そこまで変な値が設定される可能性は少ないですが、もし今のプログラムで処理できない場合は、convertMemoryLitAsByteValue内のmemory_limitをパースしている正規表現部分を修正することで対応できます。 class PHPUtils { public static function setAddedMemoryLimitByMegaByte($addition) { $current = ini_get('memory_limit'); if($current == -1) { return; } try { ini_set('memory_limit', self::calcAddedMemoryAsMegaByte($current, $addition))

PHPのNormalizerを使って文字列を正規化 (例: 全角英数字を半角英数字にする方法)

文字列検索を実施する際に、全角半角を区別せずに検索させたい場合があるかと思います。 例えば、全角文字の「ABC」で検索して半角文字の「ABC」にヒットさせたい場合です。そのような場合は、文字や文字列を正式に決められた表現に変換する処理「正規化」を行うことで解決します。(もちろん検索される側の文字列の表現が統一されていることが前提にはなります。) 「正規化」はテキストを保存する際の表記を統一するために用いられうこともあります。 PHPではNormalizerというライブラリを利用します。下記に使用例を示します。 <?php // 全角文字表記 $input = 'ABC'; $result = \Normalizer::normalize($input, \Normalizer::NFKC); // 半角文字のABCが出力される echo $result; このプログラムでNFKC正規化をすると下記のような結果が得られます。 入力 種類 出力 ㈱ 組み文字 (株) ㍇ 組み文字 マンション ア 半角カタカナ ア ちなみに正規化の種類には下記の4種類があります。( Wikipediaより引用) 。 名称 説明 NFD Normalization Form Canonical Decomposition 文字は正準等価性によって分解される。 NFC Normalization Form Canonical Composition 文字は正準等価性によって分解され、再度合成される。結果として文字の並びが変換前と変わることもありうる。 NFKD Normalization Form Compatibility Decomposition 文字は互換等価性によって分解される。 NFKC Normalization Form Compatibility Composit

Webのリクエストのパラメタを指定したパラメタ順でソートする方法(PHP)

Webリクエストのパラメタを指定したパラメタ名の順でソートするプログラムを書いてみました。 プログラム自体は汎用的なので、パラメタのソートに限らず、それ以外でも利用可能です。 <?php class ParameterSorter { private $sortOrder; // パラメタのソート順を指定 public function __construct($sortOrder) { $this->sortOrder = array_flip($sortOrder); $this->max = count($sortOrder); } // uksortを使って指定されたパラメタ順でソート // $sortOrderのキーをパラメタ名に変換して、uksortを使うのは性能を上げるためです。 // ソート順が指定されていないパラメタは、ソート順が指定されたパラメタの後になるように実装しています。 public function sort(&$parameters) { uksort($parameters, function($a, $b){ return ($this->sortOrder[$a] ?? $this->max) - ($this->sortOrder[$b] ?? $this->max); }); } // PHPのuksortは安定でない(同順位の場合もとの順序が保存されない)らしいので、元の順序を確実に保証するバージョンも作成しました。 public function stableSort(&$parameters) { $originalOrder = array_flip(array_keys($parameters)); uksort($parameters, function($a, $b) use($originalOrder){ $compared = ($this->sortOrder[$a]

MySQLのinsertとupdate用のPDOのPrepared Statementを生成するPHPのプログラム

PHPでMySQLのinsertとupdateで使えるPDOのprepared statementを生成するプログラムを書いてみました。使用頻度の高そうなパターンのSQLを生成することを目標にしてプログラムを書きました。 SQLBuilderクラス メインとなるクラスです。SQLBuilderクラスの内部で使っているColumnインタフェースとColumnインタフェースを実装したBindableColumn、FixedValueクラスは、SQLBuilderクラスの内部のみで使用することを想定しているので、SQLBuilder使用時に意識する必要はありません。 <?php class SQLBuilder { private string $table; /** * @var Column[] */ private array $columns = []; public function __construct($table) { $this->table = $table; } public function add($column, $type): self { $this->columns[] = new BindableColumn($column, $type); return $this; } public function addFixedValue($column, $value): self { $this->columns[] = new FixedValue($column, $value); return $this; } public function buildStatementForInsert() { $targetColumns = implode(', ', array_map(fn(Column $column) => $column->getName(),$this->columns));

複数の配列から要素を1つずつ選んで、すべての組み合わせを生成するPHPのプログラム

複数の配列から、要素を1つずつ選んですべての組み合わせを生成するプログラムをPHPで書いてみました。 ただし、組み合わせをすべて生成すると組み合わせ爆発を起こす可能性がありますので、使う際は本当に必要か慎重に検討してください。 <?php function iterateAllCombinations($sets, callable $callback) { $indexToKey = array_keys($sets); self::iterateAllCombinationsRecursive($sets, count($sets), $indexToKey, $callback, 0, []); } // 再帰的に呼ぶための関数本体 function iterateAllCombinationsRecursive($sets, $countOfSets, $indexToKey, callable $callback, $n, $generatedCombination) { if($n >= $countOfSets) { // この部分で生成された組み合わせを引数として、callbackが毎回呼ばれる。 $callback($generatedCombination); return; } $keyOfSet = $indexToKey[$n]; foreach($sets[$keyOfSet] as $e) { $generatedCombination[$keyOfSet] = $e; self::iterateAllCombinationsRecursive($sets, $countOfSets, $indexToKey, $callback, $n+1, $generatedCombination); } } 下記は、$callback内で生成された組み合わせをため込んだ場合の使用例になります。 <?php // 組み合わせを生成したい3つの配列 $sets = [ 'key1' => ['A', 'B',

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

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);

PHPで空のディレクトリを再帰的にすべてたたどるプログラム

PHPで指定したディレクトリにある全ディレクトリ内の空ディレクトリをすべてたどるプログラムを書いてみました。 書いた動機としては、あるディレクトリ内にあるディレクトリをすべて削除したかったためです。 symbolic linkはファイルとして扱っているので無視されます。 <?php // $callbackの引数の$fileに、空ディレクトリの\SplFileInfoが渡されて呼ばれます。 function visitEmptyDirectoryRecursively($path, callable $callback) { $files = new \DirectoryIterator($path); $containsOnlyDirectory = true; /* @var $file \SplFileInfo */ foreach($files as $file) { if($file->isDot()) { continue; } else if($file->isDir()) { if(visitEmptyDirectoryRecursively($file->getRealPath(), $callback)) { $callback($file); } else { $containsOnlyDirectory = false; } } else { $containsOnlyDirectory = false; } } return $containsOnlyDirectory; } // 空ディレクトリをすべて削除する場合は、下記のようにして使います。 // permissionの関係でディレクトリを消すことができないなどのエラー処理が必要な場合は、

PHPでCSV形式でデータを出力するためのコード

PHPでCSVファイルを出力するためのコードサンプルです。 CsvWriterクラスが本体です。最低限のメソッドを定義してあるだけなので必要に応じて拡張してみてください。 Writerインタフェースを定義して、CsvWriterクラスのコンストラクタに渡して切り替えることで、出力方式を変更することができます。 CsvWriterクラス CSV形式で出力するための本体のクラスです。このクラスのコンストラクタにWriterインタフェースを実装したクラスを渡します。 <?php class CsvWriter { private Writer $writer; private $elements = []; public function __construct(Writer $writer) { $this->writer = $writer; } public function init() { $this->writer->init(); } public function appendValuesBySpecificOrder(array $values, $keys) { foreach ($keys as $key) { $this->appendEscaped($values[$key] ?? $default); } return $this; } public function appendValues(array $values) { foreach ($values as $value) { $this->appendEscaped($value); } return $this; } public function appendAsLine(array $values) { foreac

PHPでファイルを1行ごと読み込むためのIterator

タイトル通り、PHPでファイルを1行ごと読み込むためのIteratorをSplFileObjectを使って実装してみました。 <?php class TextFileRowIterator implements \Iterator { private ?\SplFileObject $file; private $filePath; private $current; private $lineNumber = 0; public function __construct($filePath) { $this->filePath = $filePath; } public function current() { return $this->current; } public function key(): \scalar { return $this->lineNumber; } public function next(): void { $this->lineNumber++; // 応用例として、SplFileObjectの呼び出すメソッドを、fgetcsvに変えるとcsvファイルを1行ごと配列で読み込むことができます。 $this->current = str_replace(["\r", "\n"], '', $this->file->fgets()); } public function rewind(): void { $this->file = null; $this->file = new \SplFileObject($this->filePath); $this->lineNumber = 0; if(!$this->file->eof()) { $this-&g