20210730のPHPに関する記事は8件です。

【PHP】array_key_existsの使い方

array_key_exists(イグジストス)とは、配列内の指定したキーが存在するかチェックする関数 ※多次元配列で使用することはほぼない <?php $rows = [ 10 => 'PHP', 12 => 'Java', 23 => 'JavaScript', 33 => 'Ruby' ]; $start = hrtime(true); // 計測開始時間 if ( array_key_exists('23', $rows) ) { echo "配列内にMikeというキーは存在します\n"; } $end = hrtime(true); echo '処理時間:'.($end - $start).'ナノ秒'; ?> <?php $rows = [ 10 => 'PHP', 12 => 'Java', 23 => 'JavaScript', 33 => 'Ruby' ]; $start = hrtime(true); // 計測開始時間 foreach ($rows as $key => $row) { if(23 === $key){ echo "配列内にMikeというキーは存在します\n"; break; } } $end = hrtime(true); echo '処理時間:'.($end - $start).'ナノ秒'; ?>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP 不正なURLで接続してきたIPアドレスを.htaccessにDeny fromに書き込む

概要 この日記「個人情報を抜かれる危険性を認識...」に書いた通りIPアドレスをバンする処理を書いた。.htaccessを編集するのはWPのSiiteguardなどもやってるし、やってもいいでしょ。知らんけど。 処理概要 許可していないIPアドレスからadministratorやphpmyadminを含んだURLで接続してきたら.htaccessにDeny fromで書き込む こんな感じで、「START IP BANNING」~「END IP BANNING」の中を書き換える。存在しない場合は末尾に追記。 テストも適当に書いたし大丈夫じゃろ。知らんけど。 ## START IP BANNING (written by ipban.php) Order Deny,Allow # 2021-07-30 15:51:39 Deny from 111.11.11.1 # 2021-07-30 15:51:40 Deny from 111.11.11.2 ## END IP BANNING (written by ipban.php) サンプルのURL ※「これはアウト」を踏むと「これは問題ない」も拒否されるようになる これは問題ない https://yamine1san.ddns.net/phptests/apache/index.php これはアウト https://yamine1san.ddns.net/phptests/apache/phpmyadmin/index.php プログラム 呼び出し index.php <?php require 'config.php'; require 'ipban.php'; $ipban = \apache\ipban::exec($_SERVER['REMOTE_ADDR'], $_SERVER['REQUEST_URI']); if (1 === $ipban->return_code) { echo 'あなたのIPアドレス['.$_SERVER['REMOTE_ADDR'].']からの接続を禁止します。'; exit; } // 通常の処理 echo '問題の無いURLもしくは許可IPアドレスのためバン処理は何もされませんでした。'; exit; config config.php <?php namespace apache; /** * */ class config { /** * 書き込みをする.htaccessのパス */ const HTACCESS_PATH = '/opt/hoge/web/.htaccess'; /** * .htaccessに書き込む開始マーク */ const HTACCESS_IP_BAN_COMMENT_STR = '## START IP BANNING (written by ipban.php)'; /** * .htaccessに書き込む終了マーク */ const HTACCESS_IP_BAN_COMMENT_END = '## END IP BANNING (written by ipban.php)'; /** * 制限をかけないIPアドレス一覧 */ const ACCESS_RESTRICTION_ALLOWED_IPS = [ '127.0.0.1' => null, '1.2.3.4' => null, '5.6.7.8' => null, ]; /** * 一発でIPバンにするURIの正規表現 */ const ACCESS_RESTRICTION_URI_PATTERNS = [ '/\/\..*/i', '/.*(administrator|phpmyadmin|composer|nbproject|vendor|eval-stdin\.php).*/i', ]; /** * バンの期間 */ const HTACCESS_IP_BAN_PERIOD = '+1 week'; } class ipaban ipban.php <?php namespace apache; /** * */ class ipban { /** * 0以上は正常処理 * -2:.htaccessの書き込み権限が無いエラー * -1:$remote_addrのIPアドレス形式エラー * 1:バン * 2:許可IP * 3:バン対象のURIではない * * @var int */ public $return_code = 0; /** * @param $remote_addr * @param $request_uri * @return static */ public static function exec($remote_addr, $request_uri) { $ins = new static(); // IPアドレスのチェック $check_ip_result = $ins->check_ip($remote_addr, config::ACCESS_RESTRICTION_ALLOWED_IPS); if (0 > $check_ip_result) { trigger_error(__METHOD__.'(): remote_addrのチェックでエラー', E_USER_WARNING); $ins->return_code = -1; return $ins; } else if (1 === $check_ip_result) { $ins->return_code = 2; return $ins; } // URIのチェック if (true === $ins->check_uri($request_uri, config::ACCESS_RESTRICTION_URI_PATTERNS)) { $ins->return_code = 3; return $ins; } // .htaccessの作成、書き込み権限の取得 if (1 !== $ins->check_htaccess(config::HTACCESS_PATH)) { trigger_error(__METHOD__.'(): .htaccessのチェックでエラー', E_USER_WARNING); $ins->return_code = -2; return $ins; } $handle = fopen(config::HTACCESS_PATH, 'r+'); $flock = flock($handle, LOCK_EX); // ファイルロックをできなかった時はひとまず正常終了とする if (! $flock) { $ins->return_code = 4; return $ins; } $contents = stream_get_contents($handle); $new_contents = $ins->new_contents($remote_addr, $contents); // 上書き保存 ftruncate($handle, 0); rewind($handle); fputs($handle, $new_contents); fclose($handle); $ins->return_code = 1; return $ins; } /** * @param string $remote_addr * @param string $old_contents * @return string */ public function new_contents($remote_addr, $old_contents) { $contents = str_replace(["\r\n", "\r"], "\n", $old_contents); $new_contents = []; $new_contents_front = ''; $new_contents_back = ''; $new_ipban_contents = []; $new_ipban_contents[] = config::HTACCESS_IP_BAN_COMMENT_STR; $new_ipban_contents[] = 'Order Deny,Allow'; $str_pos = strpos($contents, config::HTACCESS_IP_BAN_COMMENT_STR); $end_pos = strpos($contents, config::HTACCESS_IP_BAN_COMMENT_END); // 開始コメントが存在する時 if (FALSE !== $str_pos) { $new_contents_front = substr($contents, 0, $str_pos - 1); if (FALSE !== $end_pos) { $new_contents_back = substr($contents, $end_pos + strlen(config::HTACCESS_IP_BAN_COMMENT_END) + 1); } $ipban_contents_arr = explode("\n", substr($contents, $str_pos)); $ipban_list_tmp = []; $i = 0; // IPアドレス追加時にインクリメント $expired = 0; foreach ($ipban_contents_arr as $str) { // "期限切れ"はipban_list_tmpに入れない if (1 === $expired) { $expired = 0; continue; } // # 2021-07-30 00:00:00 if (preg_match('/^# [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/', $str)) { // "期限切れ"はipban_list_tmpに入れない if (time() > strtotime(config::HTACCESS_IP_BAN_PERIOD, strtotime(substr($str, 2)))) { $expired = 1; continue; } $ipban_list_tmp[$i]['period'] = $str; } // # Deny from 192.168.0.11 if (preg_match('/^Deny from (([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/', $str)) { $ipban_list_tmp[$i]['deny'] = $str; $i++; } } // ipban_list_tmpをipアドレスをキー値にして重複削除 $ipban_list_map = []; foreach ($ipban_list_tmp as $arr) { // 期限「# 2021-07-30 00:00:00」が連続してコメントに存在した時に発生する処理 if (! array_key_exists('deny', $arr)) { continue; } $ipban_list_map[$arr['deny']] = $arr; } $ipban_list_map['Deny from '.$remote_addr]['period'] = '# '.date('Y-m-d H:i:s'); $ipban_list_map['Deny from '.$remote_addr]['deny'] = 'Deny from '.$remote_addr; // new_ipban_contentsへ foreach ($ipban_list_map as $arr) { // IPアドレスのみ(「Deny from 192.168.0.11」が連続)で期限が無い場合はperiodが無いので無期限として追加 $new_ipban_contents[] = array_key_exists('period', $arr) ? $arr['period'] : '# Indefinite period'; $new_ipban_contents[] = $arr['deny']; } } // 開始コメントが存在しない時 else { $new_contents[] = $contents; $new_ipban_contents[] = '# '.date('Y-m-d H:i:s'); $new_ipban_contents[] = 'Deny from '.$remote_addr; } if ('' !== $new_contents_front) { $new_contents[] = $new_contents_front; } $new_ipban_contents[] = config::HTACCESS_IP_BAN_COMMENT_END; $new_contents[] = implode("\n", $new_ipban_contents); if ('' !== $new_contents_back) { $new_contents[] = $new_contents_back; } return implode("\n", $new_contents); } /** * IPアドレスのチェック(CodeIgniterのForm_validationより) * * @param string $ip * @param string $options 'ipv4' or 'ipv6' to validate a specific IP format * @return bool */ public function valid_ip($ip, $options = null) { if (! is_null($options)) { $which = strtolower($options); if ('ipv4' === $which) { $options = 1048576; } else if ('ipv6' === $which) { $options = 2097152; } } return (bool)filter_var($ip, 275, $options); } /** * @param string $ip * @param array $allowed_ips * @return int */ public function check_ip($ip, $allowed_ips) { if (! $this->valid_ip($ip, 'ipv4')) { return -1; } if (array_key_exists($ip, $allowed_ips)) { return 1; } return 0; } /** * @param string $uri * @param array $patterns * @return bool */ public function check_uri($uri, $patterns) { foreach ($patterns as $pattern) { if (preg_match($pattern, $uri)) { return false; } } return true; } /** * @param string $path * @return int */ public function check_htaccess($path) { if (! is_file($path)) { @touch($path); } if (! is_file($path)) { return -1; } if (! is_readable($path) || ! is_writable($path)) { // TODO テスト $user = exec('whoami'); if ($user) { @chown($path, $user); @chmod($path, 0644); } } if (! is_readable($path) || ! is_writable($path)) { return -2; } return 1; } } テスト ipbanTest.php <?php use apache\ipban; use apache\config; use PHPUnit\Framework\TestCase; /** * Class gethmsTest */ class ipbanTest extends TestCase { /** * */ public function test_check_ip() { $ipban = new ipban(); // IPアドレス形式エラー $this->assertSame(-1, $ipban->check_ip('zzzzzzzzzz', [])); // 許可IP $this->assertSame(1, $ipban->check_ip('127.0.0.1', ['127.0.0.1' => null])); // 許可していないIP $this->assertSame(0, $ipban->check_ip('127.0.0.2', ['127.0.0.1' => null])); } /** * */ public function test_check_uri() { $ipban = new ipban(); // アウト $this->assertSame(false, $ipban->check_uri('/abcd/.git/config', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/.project', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/composer.json', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/admin/phpmyadmin', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/administrator/user-list', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/vendor/phpunit', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/eval-stdin.php', config::ACCESS_RESTRICTION_URI_PATTERNS)); // 大丈夫 $this->assertSame(true, $ipban->check_uri('/yamine1san', config::ACCESS_RESTRICTION_URI_PATTERNS)); } /** * */ public function test_new_contents() { $ipban = new ipban(); // 追加される事の確認 $old_contents = ""; $new_contents = $ipban->new_contents('192.168.0.1', $old_contents); $this->assertSame(true, (0 < strpos($new_contents, '# '.date('Y-m-d H:i:s')))); $this->assertSame(true, (0 < strpos($new_contents, 'Deny from 192.168.0.1'))); $old_period = '# '.date('Y-m-d H:i:s', strtotime('-1 day')); // 既に存在する時に期限が更新される事、前後の内容が消されない事の確認 $old_contents = " #aaaaaaaaaaaaaa ".config::HTACCESS_IP_BAN_COMMENT_STR." {$old_period} Deny from 192.168.0.1 ".config::HTACCESS_IP_BAN_COMMENT_END." #bbbbbbbbbbbbbb "; $new_contents = $ipban->new_contents('192.168.0.1', $old_contents); // 前の内容が残っている事 $this->assertSame(true, (0 < strpos($new_contents, '#aaaaaaaaaaaaaa'))); // 古い期限が消えた事 $this->assertSame(false, strpos($new_contents, $old_period)); // 新しい期限になった事 $this->assertSame(true, (0 < strpos($new_contents, '# '.date('Y-m-d H:i:s')))); $this->assertSame(true, (0 < strpos($new_contents, 'Deny from 192.168.0.1'))); // 後の内容が残っている事 $this->assertSame(true, (0 < strpos($new_contents, '#bbbbbbbbbbbbbb'))); // 順番が正しい $this->assertSame(true, strpos($new_contents, '#aaaaaaaaaaaaaa') < strpos($new_contents, '#bbbbbbbbbbbbbb')); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP 不正なURLで接続してきたIPアドレスを.htaccessのDeny fromに書き込む

概要 この日記「個人情報を抜かれる危険性を認識...」に書いた通りIPアドレスをバンする処理を書いた。.htaccessを編集するのはWPのSiiteguardなどもやってるし、やってもいいでしょ。知らんけど。 処理概要 許可していないIPアドレスからadministratorやphpmyadminを含んだURLで接続してきたら.htaccessにDeny fromで書き込む。こんな感じで、「START IP BANNING」~「END IP BANNING」の中を書き換える。存在しない場合は末尾に追記。テストも適当に書いたし大丈夫じゃろ。知らんけど。 ## START IP BANNING (written by ipban.php) Order Deny,Allow # 2021-07-30 15:51:39 Deny from 111.11.11.1 # 2021-07-30 15:51:40 Deny from 111.11.11.2 ## END IP BANNING (written by ipban.php) サンプルのURL ※「これはアウト」を踏むと「これは問題ない」も拒否されるようになる これは問題ない https://yamine1san.ddns.net/phptests/apache/index.php これはアウト https://yamine1san.ddns.net/phptests/apache/phpmyadmin/ プログラム 呼び出し index.php <?php require 'config.php'; require 'ipban.php'; $ipban = \apache\ipban::exec($_SERVER['REMOTE_ADDR'], $_SERVER['REQUEST_URI']); if (1 === $ipban->return_code) { echo 'あなたのIPアドレス['.$_SERVER['REMOTE_ADDR'].']からの接続を禁止します。'; exit; } // 通常の処理 echo '問題の無いURLもしくは許可IPアドレスのためバン処理は何もされませんでした。'; exit; config config.php <?php namespace apache; /** * */ class config { /** * 書き込みをする.htaccessのパス */ const HTACCESS_PATH = '/opt/hoge/web/.htaccess'; /** * .htaccessに書き込む開始マーク */ const HTACCESS_IP_BAN_COMMENT_STR = '## START IP BANNING (written by ipban.php)'; /** * .htaccessに書き込む終了マーク */ const HTACCESS_IP_BAN_COMMENT_END = '## END IP BANNING (written by ipban.php)'; /** * 制限をかけないIPアドレス一覧 */ const ACCESS_RESTRICTION_ALLOWED_IPS = [ '127.0.0.1' => null, '1.2.3.4' => null, '5.6.7.8' => null, ]; /** * 一発でIPバンにするURIの正規表現 */ const ACCESS_RESTRICTION_URI_PATTERNS = [ '/\/\..*/i', '/.*(administrator|phpmyadmin|composer|nbproject|vendor|eval-stdin\.php).*/i', ]; /** * バンの期間 */ const HTACCESS_IP_BAN_PERIOD = '+1 week'; } class ipaban ipban.php <?php namespace apache; /** * */ class ipban { /** * 0以上は正常処理 * -2:.htaccessの書き込み権限が無いエラー * -1:$remote_addrのIPアドレス形式エラー * 1:バン * 2:許可IP * 3:バン対象のURIではない * * @var int */ public $return_code = 0; /** * @param $remote_addr * @param $request_uri * @return static */ public static function exec($remote_addr, $request_uri) { $ins = new static(); // IPアドレスのチェック $check_ip_result = $ins->check_ip($remote_addr, config::ACCESS_RESTRICTION_ALLOWED_IPS); if (0 > $check_ip_result) { trigger_error(__METHOD__.'(): remote_addrのチェックでエラー', E_USER_WARNING); $ins->return_code = -1; return $ins; } else if (1 === $check_ip_result) { $ins->return_code = 2; return $ins; } // URIのチェック if (true === $ins->check_uri($request_uri, config::ACCESS_RESTRICTION_URI_PATTERNS)) { $ins->return_code = 3; return $ins; } // .htaccessの作成、書き込み権限の取得 if (1 !== $ins->check_htaccess(config::HTACCESS_PATH)) { trigger_error(__METHOD__.'(): .htaccessのチェックでエラー', E_USER_WARNING); $ins->return_code = -2; return $ins; } $handle = fopen(config::HTACCESS_PATH, 'r+'); $flock = flock($handle, LOCK_EX); // ファイルロックをできなかった時はひとまず正常終了とする if (! $flock) { $ins->return_code = 4; return $ins; } $contents = stream_get_contents($handle); $new_contents = $ins->new_contents($remote_addr, $contents); // 上書き保存 ftruncate($handle, 0); rewind($handle); fputs($handle, $new_contents); fclose($handle); $ins->return_code = 1; return $ins; } /** * @param string $remote_addr * @param string $old_contents * @return string */ public function new_contents($remote_addr, $old_contents) { $contents = str_replace(["\r\n", "\r"], "\n", $old_contents); $new_contents = []; $new_contents_front = ''; $new_contents_back = ''; $new_ipban_contents = []; $new_ipban_contents[] = config::HTACCESS_IP_BAN_COMMENT_STR; $new_ipban_contents[] = 'Order Deny,Allow'; $str_pos = strpos($contents, config::HTACCESS_IP_BAN_COMMENT_STR); $end_pos = strpos($contents, config::HTACCESS_IP_BAN_COMMENT_END); // 開始コメントが存在する時 if (FALSE !== $str_pos) { $new_contents_front = substr($contents, 0, $str_pos - 1); if (FALSE !== $end_pos) { $new_contents_back = substr($contents, $end_pos + strlen(config::HTACCESS_IP_BAN_COMMENT_END) + 1); } $ipban_contents_arr = explode("\n", substr($contents, $str_pos)); $ipban_list_tmp = []; $i = 0; // IPアドレス追加時にインクリメント $expired = 0; foreach ($ipban_contents_arr as $str) { // "期限切れ"はipban_list_tmpに入れない if (1 === $expired) { $expired = 0; continue; } // # 2021-07-30 00:00:00 if (preg_match('/^# [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/', $str)) { // "期限切れ"はipban_list_tmpに入れない if (time() > strtotime(config::HTACCESS_IP_BAN_PERIOD, strtotime(substr($str, 2)))) { $expired = 1; continue; } $ipban_list_tmp[$i]['period'] = $str; } // # Deny from 192.168.0.11 if (preg_match('/^Deny from (([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/', $str)) { $ipban_list_tmp[$i]['deny'] = $str; $i++; } } // ipban_list_tmpをipアドレスをキー値にして重複削除 $ipban_list_map = []; foreach ($ipban_list_tmp as $arr) { // 期限「# 2021-07-30 00:00:00」が連続してコメントに存在した時に発生する処理 if (! array_key_exists('deny', $arr)) { continue; } $ipban_list_map[$arr['deny']] = $arr; } $ipban_list_map['Deny from '.$remote_addr]['period'] = '# '.date('Y-m-d H:i:s'); $ipban_list_map['Deny from '.$remote_addr]['deny'] = 'Deny from '.$remote_addr; // new_ipban_contentsへ foreach ($ipban_list_map as $arr) { // IPアドレスのみ(「Deny from 192.168.0.11」が連続)で期限が無い場合はperiodが無いので無期限として追加 $new_ipban_contents[] = array_key_exists('period', $arr) ? $arr['period'] : '# Indefinite period'; $new_ipban_contents[] = $arr['deny']; } } // 開始コメントが存在しない時 else { $new_contents[] = $contents; $new_ipban_contents[] = '# '.date('Y-m-d H:i:s'); $new_ipban_contents[] = 'Deny from '.$remote_addr; } if ('' !== $new_contents_front) { $new_contents[] = $new_contents_front; } $new_ipban_contents[] = config::HTACCESS_IP_BAN_COMMENT_END; $new_contents[] = implode("\n", $new_ipban_contents); if ('' !== $new_contents_back) { $new_contents[] = $new_contents_back; } return implode("\n", $new_contents); } /** * IPアドレスのチェック(CodeIgniterのForm_validationより) * * @param string $ip * @param string $options 'ipv4' or 'ipv6' to validate a specific IP format * @return bool */ public function valid_ip($ip, $options = null) { if (! is_null($options)) { $which = strtolower($options); if ('ipv4' === $which) { $options = 1048576; } else if ('ipv6' === $which) { $options = 2097152; } } return (bool)filter_var($ip, 275, $options); } /** * @param string $ip * @param array $allowed_ips * @return int */ public function check_ip($ip, $allowed_ips) { if (! $this->valid_ip($ip, 'ipv4')) { return -1; } if (array_key_exists($ip, $allowed_ips)) { return 1; } return 0; } /** * @param string $uri * @param array $patterns * @return bool */ public function check_uri($uri, $patterns) { foreach ($patterns as $pattern) { if (preg_match($pattern, $uri)) { return false; } } return true; } /** * @param string $path * @return int */ public function check_htaccess($path) { if (! is_file($path)) { @touch($path); } if (! is_file($path)) { return -1; } if (! is_readable($path) || ! is_writable($path)) { // TODO テスト $user = exec('whoami'); if ($user) { @chown($path, $user); } @chmod($path, 0644); } if (! is_readable($path) || ! is_writable($path)) { return -2; } return 1; } } テスト ipbanTest.php <?php use apache\ipban; use apache\config; use PHPUnit\Framework\TestCase; /** * Class ipbanTest */ class ipbanTest extends TestCase { /** * */ public function test_check_ip() { $ipban = new ipban(); // IPアドレス形式エラー $this->assertSame(-1, $ipban->check_ip('zzzzzzzzzz', [])); // 許可IP $this->assertSame(1, $ipban->check_ip('127.0.0.1', ['127.0.0.1' => null])); // 許可していないIP $this->assertSame(0, $ipban->check_ip('127.0.0.2', ['127.0.0.1' => null])); } /** * */ public function test_check_uri() { $ipban = new ipban(); // アウト $this->assertSame(false, $ipban->check_uri('/abcd/.git/config', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/.project', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/composer.json', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/admin/phpmyadmin', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/administrator/user-list', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/vendor/phpunit', config::ACCESS_RESTRICTION_URI_PATTERNS)); $this->assertSame(false, $ipban->check_uri('/eval-stdin.php', config::ACCESS_RESTRICTION_URI_PATTERNS)); // 大丈夫 $this->assertSame(true, $ipban->check_uri('/yamine1san', config::ACCESS_RESTRICTION_URI_PATTERNS)); } /** * */ public function test_new_contents() { $ipban = new ipban(); // 追加される事の確認 $old_contents = ""; $new_contents = $ipban->new_contents('192.168.0.1', $old_contents); $this->assertSame(true, (0 < strpos($new_contents, '# '.date('Y-m-d H:i:s')))); $this->assertSame(true, (0 < strpos($new_contents, 'Deny from 192.168.0.1'))); $old_period = '# '.date('Y-m-d H:i:s', strtotime('-1 day')); // 既に存在する時に期限が更新される事、前後の内容が消されない事の確認 $old_contents = " #aaaaaaaaaaaaaa ".config::HTACCESS_IP_BAN_COMMENT_STR." {$old_period} Deny from 192.168.0.1 ".config::HTACCESS_IP_BAN_COMMENT_END." #bbbbbbbbbbbbbb "; $new_contents = $ipban->new_contents('192.168.0.1', $old_contents); // 前の内容が残っている事 $this->assertSame(true, (0 < strpos($new_contents, '#aaaaaaaaaaaaaa'))); // 古い期限が消えた事 $this->assertSame(false, strpos($new_contents, $old_period)); // 新しい期限になった事 $this->assertSame(true, (0 < strpos($new_contents, '# '.date('Y-m-d H:i:s')))); $this->assertSame(true, (0 < strpos($new_contents, 'Deny from 192.168.0.1'))); // 後の内容が残っている事 $this->assertSame(true, (0 < strpos($new_contents, '#bbbbbbbbbbbbbb'))); // 順番が正しい $this->assertSame(true, strpos($new_contents, '#aaaaaaaaaaaaaa') < strpos($new_contents, '#bbbbbbbbbbbbbb')); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】配列から一つのカラムを取り出す、及びキー名をカラム名にする

<?php $rows = [ 0 => [ 'id' => 10, 'language' => 'PHP', 'framework' => 'Laravel'], 1 => [ 'id' => 20, 'language' => 'Java', 'framework' => 'Spring'], 2 => [ 'id' => 30, 'language' => 'JavaScript', 'framework' => 'Vue.js' ], 3 => [ 'id' => 40, 'language' => 'Ruby', 'framework' => 'Ruby on Rails' ] ]; $abc = array_column($rows, 'language', 'id'); var_dump($abc); ?> もしくは <?php foreach($rows as $row ){ $ddd[$row['id']] = $row['language']; }; ?> 出力結果 array(4) { [10]=> string(3) "PHP" [20]=> string(4) "Java" [30]=> string(10) "JavaScript" [40]=> string(4) "Ruby" }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

定期的にモデル削除のPrunable機能到着 [Laravel 8.50]

概念 定期的に不要になったレコードをDBから物理的に削除したい場合があるとおもいます。今までこの処理をバッチやコマンドで作る必要がありましたがLaravel 8.50のリリースに含んでいるPrunableかMassPrunableのトレイトをモデルクラスに追加することで、この処理が簡単に実装できます。 セットアップ 変更履歴のログや調査のため、ユーザーの名前などの情報が変更されるときにDBのUserLogのテーブルに保存する事を例とします。UserLog情報を6ヶ月間保存する必要があり、定期的に6ヶ月間前のレコードをDBから物理的に削除します。 使い方 モデルの周り モデルにPrunableのトレイトを追加して、prunableの関数に削除対象のレコードをEloquentのクエリビルダで返す機能を実装します。PrunableとMassPrunableを使うとき論理削除のSoftDeletesを利用しているモデルでもクエリに一致した場合、物理的にforceDelete()で削除されますので気を付けてください。 <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Prunable; class UserLog extends Model { use Prunable; /** * 削除対処のモデルを取得 * * @return \Illuminate\Database\Eloquent\Builder */ public function prunable() { // 6ヶ月前のレコードを取得する return static::where('created_at', '<=', now()->subMonths(6)); } } もしそのモデルと関連する処理(たとえば、古いプロファイルの写真を削除することや、関連するモデル削除など)が必要な場合、モデルが削除する前にpruningという関数で処理が実装できます。 /** * モデルの削除の準備 * * @return void */ protected function pruning() { // ここでモデルが削除する前に必要な処理を実装します } スケジュール Prunableを利用しているモデルの削除処理が行われるため、アプリケーションのApp\Console\Kernelクラスでmodel:pruneのArtisanコマンドを登録する必要があります。そのコマンドが実行されるとapp/Modelsディレクトリ内にあるPrunable とMassPrunableを利用しているモデルの削除処理が行われます。もしモデルが別のダイレクトリーにあるなら、--modelのオプションを使って、モデルのクラス名を配列として指定できます。下記の例だと毎日の03:00に削除処理が実行されます。 App\Console\Kernel.php protected function schedule(Schedule $schedule) { // `app/Models`ディレクトリ内の場合 $schedule->command('model:prune') ->daily() ->at('03:00'); // `app/Models`ディレクトリ以外の場合 $schedule->command('model:prune', [ '--model' => [UserLog::class], ])->daily() ->at('03:00'); } MassPrunable VS Prunable MassPrunableというのはの複数削除処理が行われ、Prunableと違って削除前にレコードを取得せず、複数レコード削除クエリが実行されます。そのためMassPrunableの方がPrunableより、処理が効率的です。しかし、削除する前にレコードが取得されないため、Eloquentのdeleted・deleting・pruningのイベントは発火しません。それ以外は、Prunableの実装と同じです。 <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\MassPrunable; class UserLog extends Model { use MassPrunable; /** * 削除対処のモデルを取得 * * @return \Illuminate\Database\Eloquent\Builder */ public function prunable() { // 6ヶ月前のレコードを取得する return static::where('created_at', '<=', now()->subMonths(6)); } /** * モデルの削除の準備 * * @return void */ protected function pruning() { // イベントが発生されないので、この関数の処理が行われない } } 参考 https://github.com/laravel/framework/pull/37696 https://laravel.com/docs/8.x/eloquent#pruning-models https://laravel.com/api/8.x/Illuminate/Database/Eloquent/Prunable.html https://laravel.com/api/8.x/Illuminate/Database/Eloquent/MassPrunable.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【php】配列の中身の0を削除する ※array_filter()を使う

<?php $arr = [1,2,3,'あああ','00456','123abc',-3,0,0,0,0]; $arr = array_filter($arr); var_dump($arr); ?> 出力結果 array(7) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> string(9) "あああ" [4]=> string(5) "00456" [5]=> string(6) "123abc" [6]=> int(-3) }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【php】配列の中身の0と空要素を削除する ※array_filter()を使う

処理時間:1758ナノ秒 <?php $arr = [1,2,3,'あああ','00456','123abc',-3,0,0,0,'']; $start = hrtime(true); // 計測開始時間 $arr = array_filter($arr); $end = hrtime(true); echo '処理時間:'.($end - $start).'ナノ秒'; var_dump($arr); ?> もしくは 処理時間:6726ナノ秒 <?php $arrs = [1,2,3,'あああ','00456','123abc',-3,0,0,0,'']; $start = hrtime(true); // 計測開始時間 foreach($arrs as $arr ){ if(!empty($arr)){ $result[] = $arr; }; }; $end = hrtime(true); echo '処理時間:'.($end - $start).'ナノ秒'; var_dump($result); ?> 出力結果 array(7) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> string(9) "あああ" [4]=> string(5) "00456" [5]=> string(6) "123abc" [6]=> int(-3) }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】配列の中身をintに置き換える

<?php $arr = [1,2,3,'あああ','00456','123abc',-3,' ']; $arr = array_map('intval', $arr); var_dump($arr); ?> 出力結果 array(8) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(0)←あああ [4]=> int(456)←00456 [5]=> int(123)←123abc [6]=> int(-3) }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む