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

投稿

7月, 2020の投稿を表示しています

register_shutdown_functionを使ってPHPのFatal Error発生後の処理を制御

PHP 7以降では、PHPのエラーは基本的にErrorクラスとして扱われ、try-catchブロックで捕まえることができるようになりました。(Catchable Fatal Error) ちなみにPHP 7では下記の2つのクラスがThrowableクラスを継承しており、両方捕まえるときは、try-catchブロックでThrowableをcatchするように書けばOKです。 Exception Error ただし、一部のFatal Error(例: Memory Exhausted Error)はtry-catchブロックで捕まえることができません。 Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 9792 bytes) in .... この場合は、register_shutdown_functionを使って、PHPのプログラムが終了したときにエラー処理をするしかなさそうです。 register_shutdown_functionとerror_get_lastを使ってエラー処理をする関数のコード例を示します。 <?php // PHPのError定数 => 文字列表現の連想配列 const ERROR_CODE_TO_STRING = [ E_ERROR => 'E_ERROR', E_WARNING => 'E_WARNING', E_PARSE => 'E_PARSE', E_NOTICE => 'E_NOTICE', E_CORE_ERROR => 'E_CORE_ERROR', E_CORE_WARNING => 'E_CORE_WARNING', E_COMPILE_WARNING => 'E_COMPILE_WARNING', E_USER_ERROR => 'E_USER_ERROR', E_USER_WARNING => ...

Linuxで特定のパターンにマッチするファイルの削除方法

Linuxで特定のパターンにマッチするファイルの削除方法について簡単にメモ 例1: /tmpディレクトリ配下の128bit hash(例:e111c876b32143abccd98bb56542bcc2)のパターンにマッチするファイルをすべて削除 regextypeを指定しないと我々が普段(?)よく知っているposixのregexが使えないことに注意! find /tmp -type f -regextype posix-egrep -regex '\./[0-9a-f]{32}' -delete 例2: /tmpディレクトリ配下の、AAAで始まるディレクトリで、更新日が5日以前のものを、ディレクトリの中身のファイルも含めてすべて削除 最後の「-exec {} +」は複数のファイルをまとめてコマンド実行するという意味 「-exec {} ;」の場合はファイル一個ずつの処理になるので、rmの場合は「-exec {} +」の方が効率が良い。 find /tmp -mindepth 1 -maxdepth -type d -mtime +5 -name ".AAA*" -exec rm -rf {} +

PHPでファイルを指定した行数ごとに分割

ファイルを指定した行数ごとに分割するためには、Linuxのsplitコマンドを使えば簡単に実現できます。 PHPではexec関数にsplitコマンドを渡して実行すればよいですが、下記の弱点があります。 Linuxのコマンドに依存 (PHPの場合はほとんどLinux環境で動作させることが普通なのでそこまで問題にならないかも知れません)。 exec関数は慎重に引数を渡さないと、OSコマンドインジェクション脆弱性を引き起こす可能性がある。 そこで、今回はPHPでファイルを指定した行数ごとに分割するプログラムを書いてみました。 <?php class FileSplitter { private $lines; private $fileCount; public function split($filePath, $linesPerFile, $outputDir) { $this->fileCount = 0; $this->lines = null; $file = new \SplFileObject($filePath); $lineCount = 0; try{ while (!$file->eof()) { if($lineCount % $linesPerFile === 0) { $this->writeToFile($this->generateOutputFilePath($outputDir, $file)); } $this->lines[] = $file->fgets(); $lineCount++; } $this->writeToFile($this->generateOutpu...

Bashでオプション引数をとる方法

下記の例で、変数A, B, Cにbashからオプション引数を割り当てる方法示します。 #!/bin/bash set -e set -o pipefail OPT=`getopt a:b:c: $*` set -- $OPT for i; do case $i in --) shift; break ;; -a) A=$2; shift; shift;; -b) B=$2; shift; shift;; -c) C=$2; shift; shift;; esac done # check if A, B and C are properly given from option. if [ -z "$A" ]; then echo "A is not specified"; exit; fi if [ -z "$B" ]; then echo "B is not specified"; exit; fi if [ -z "$C" ]; then echo "C is not specified"; exit; fi

MySQLでGROUP_CONCATしたフィールドに対して疑似的にLIMITを実現する方法

MySQLでGROUP_CONCATしたフィールドに対して疑似的にLIMITを実現するには、GROUP_CONCATで生成された文字列に対して、SUBSTRING_INDEXを使って文字列を切り出す方法が簡単です。 # 下記はid, codeをカラムに持つテーブルで、codeカラムでGROUP BYして、codeごとにidをlimitで取得する例です。 SELECT code ,SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY id DESC), ',', :limit) # :limitの部分に取得したい件数を指定。 FROM table GROUP BY code 長所は、下記のように条件を指定して、LIMIT句で取得件数を指定したクエリを何度も発行する必要がないところです。 特に、一回あたりクエリの発行コストが高い場合は、GROUP_CONCATを使って一度に取得したほうが最終的な実行時間をかなり節約することができます。 SELECT id FROM table WHERE code = 'A' ORDER BY id DESC LIMIT :limit; ただし、短所も多いので、使用する際は、これらの短所について十分に考慮したうえで使ってください。 GROUP_CONCATで生成された文字列に対して、SUBSTRING_INDEXを使って文字列を切り出すという文字列処理なので、無駄が多い。 特にGROUP_CONCATで生成された元の文字列が長い場合。 GROUP_CONCATの区切り文字が、GROUP_CONCATされる元の文字列に含まれていると正しくLIMITされない。 例:GROUP_CONCATされる元の文字列にカンマが含まれているのに、カンマを区切り文字で指定している場合。 GROUP_CONCATの最大文字数制限を超えた場合は、機能しない。 MySQLのGROUP_CONCATの最大文字数制限は、「SHOW VARIABLES LIKE '%group_concat%';」で調べられます。デフォルト値は1024のようです。 ...

MySQLでサブクエリで先に集計してクエリの実行を高速化

サブクエリを用いたクエリの最適化 巨大なテーブルをFull Table Scanして件数を集計する場合に、JOINの少ないサブクエリで先に集計して後から他のテーブルをJOINすると、クエリの実行を高速化できる場合があります。 特にJOINするテーブルが多い時には実行時間に大きな差が出ます。 もちろん、データやINDEXの張り方にも依存するで、クエリを書き換える前にEXPLAINで必ずRows_examinedなどが減ることを確認することは必須です。 この記事では、huge_rows_tableに大量の行があり、他のテーブルの行数はそれほどない場合を想定したクエリ例を示します。 想定するテーブル CREATE TABLE `huge_rows_table` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `group_id_1` bigint(20) NOT NULL, `group_id_2` bigint(20) NOT NULL, `check_flg` tinyint(4) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `IDX_group_id_1` (`group_id_1`), KEY `IDX_group_id_2` (`group_id_2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; CREATE TABLE `group_table_1` ( `id` bigint(20) NOT NULL, `name` varchar(500) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; CREATE TABLE `group_table_2` ( `id` bigint(20) NOT NULL, `name` varchar(500) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; クエリ # 元クエリ SELECT gt1.name AS g...

Linuxで特定のディスクの使用率を調べるコマンド

Linuxで特定のディスクの使用率は下記のコマンドで調べられます。 df {対象のディレクトリパス} | awk '{print $5}' | sed -ne 2p | cut -d"%" -f1 応用で、Bashを使って下記の機能を実現させてみます。 対象ディレクトリを指定できるように 制限値より多い場合に実行失敗にする #!/bin/bash checkDiskUsage() { local $directory=$1 local $limit=$2 # 下記のコマンドの前にssh hostのようにすれば、リモートホストでも実行可能。 local used=`df $directory | awk '{print $5}' | sed -ne 2p | cut -d"%" -f1` echo "Disk usage of $directory: $used%" if [ $used -gt $3 ] then echo "Exceed limit of disk usage (greather than $limit%)" exit 1 fi } checkDiskUsage "/tmp" 80

PHPで現在設定されているerror reporting levelを文字列で表示

PHPのerror reporting levelは、bit形式で表現されていて、error_reporting関数で取得しても、何が設定されているのかすぐにはわかりづらいと思います。 ちょっとしたプログラムですが、error_reporting_levelを文字列形式で出力するプログラムを書いてみました。 <?php class PHPUtils { private const ERROR_CODE_TO_STRING = [ E_ERROR => 'E_ERROR', E_WARNING => 'E_WARNING', E_PARSE => 'E_PARSE', E_NOTICE => 'E_NOTICE', E_CORE_ERROR => 'E_CORE_ERROR', E_CORE_WARNING => 'E_CORE_WARNING', E_COMPILE_WARNING => 'E_COMPILE_WARNING', E_USER_ERROR => 'E_USER_ERROR', E_USER_WARNING => 'E_USER_WARNING', E_USER_NOTICE => 'E_USER_NOTICE', E_STRICT => 'E_STRICT', E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', E_DEPRECATED => 'E_DEPRECATED', E_USER_DEPRECATED => 'E_USER_DEPRECATED', ]; public static function getCurr...