PHP 個人的な技術メモ

mb_strimwidth

echo mb_strimwidth("Hello World", 0, 10, '...', 'UTF-8');

strpos

$pos = strpos($mystring, $findme);

preg_match

if (preg_match('/\A[0-9]+\z/', $str)) {
    // 数字のみ
}

JSONエンコード

// over PHP5.4.0
$json = json_encode($str, JSON_UNESCAPED_UNICODE);
$json = json_encode($str, JSON_PRETTY_PRINT);
$json = json_encode($str, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

ブラウザキャッシュ無効

// ブラウザキャッシュ無効
header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);

配列キー存在チェック

array_key_exists('first', $searchArray))

引数処理

if (count($argv) !== 2) {
   echo '[USED] php decode.php {log_file}'.PHP_EOL;
   exit;
}
echo $argv[1];

preg_match_all

preg_match_all('/<A>(.*?)<\/A>/', $str, $matches, PREG_SET_ORDER);

数値文字参照デコード

echo mb_convert_encoding('&#x82C6;', 'UTF-8', 'HTML-ENTITIES');

CGIパラメータ構築

$params = array( 'param1'=>'aaa', 'param2'=>'bbb', 'param3'=>'ccc');
echo 'index.php?' . http_build_query($params);
index.php?param1=aaa&param2=bbb&param3=cc

日付ログファイルパス YYYY/MM/DD

define('LOGFILE_PATH', dirname(__FILE__).'/log/'.date('Y/m/d').'/service.log');

日時 YYYY/MM/DD HH:MM:SS

date('Y/m/d H:i:s')
date('Ymd', strtotime('-1 day', time()))

複数半角スペース削除

// 両サイドのスペースを消す
$str = trim($str);
// 改行、タブをスペースへ
$str = preg_replace('/[\n\r\t]/', '', $str);
// 複数スペースを一つへ
$str = preg_replace('/\s(?=\s)/', '', $str);

時刻取得

// 01:02:03(時を24時間表記し、2桁ゼロ詰めする場合)
echo date('H:i:s');
// 1時02分03秒(時を24時間表記し、2桁ゼロ詰めしない場合)
echo date('G:i:s:');

エラー表示

PHP

ini_set("display_errors", On);
error_reporting(E_ALL | E_STRICT);

.htaccess

php_flag  display_errors On

タイムゾーンの設定

date_default_timezone_set('Asia/Tokyo');

ファイル存在チェック&ファイルオープン

$tmpFile = '/tmp/hogehoge.txt';
if (!file_exists($tmpFile)) {
    $fp = fopen($tmpFile, 'w');
    if ($fp === false) return false;
    fwrite($fp, 'HogeHoge');
    fclose($fp);
    
    return true;
}

サーバ名で処理を切り替え(開発機等)

if (strpos($_SERVER["HTTP_HOST"], 'develop.') !== false) {
    // 処理
}

IP制限

// 許可IP
$allowIps = array(
    'xx.xxx.xxx.xxx',
    'xx.xxx.xxx.xxx',
);

// アクセス元IPチェック
if (! in_array($_SERVER['REMOTE_ADDR'], $allowIps)) {
       header('HTTP', true, 500);
       exit;
}

IP制限 CIDR形式

function inCidr($targetIp, $allowIps)
{
   if (empty($allowIps)) return false;
   foreach($allowIps as $allowIp){
       if (empty($allowIp)) continue;
       list($pIp, $maskBit) = explode('/', $allowIp);
       $numTarget = ip2long($pIp) >> (32 - $maskBit);
       $numAllow = ip2long($targetIp) >> (32 - $maskBit);
       if ($numTarget === $numAllow) return true;
   }
   return false;
}

数値化文字(16進数文字)をデコード

html_entity_decode('&#x300e;&#x4e88;', ENT_NOQUOTES, 'UTF-8');

XSS対策エスケース関数

function h($text)
{
    if (is_array($text)) return array_map('h', $text);
    return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}

例外ハンドラ(PHP5以降)

例外は、exception_handler がコールされた後に 停止します。

function exceptionHandler($e) {
   var_dump($e->getMessage());
}

set_exception_handler('exceptionHandler');

$db = DB::init($config);
    
$db->setSql('SELECT * FROM ROD_BOOKS WHERE ID IN (:hoge)');
$db->setIn('hoge', array(56,57,58,59,60,41));
 
$db->exec();
// 明示的な例外発生
throw new Exception('HogeHoge_Error');

STRERRにエラー出力

/**
 * エラーハンドラー関数
 */
function exceptionHandler($e)
{
   // 標準エラーに出力
   $stderr = fopen('php://stderr', 'w');
   fwrite($stderr, $e->getMessage());
   fclose($stderr);
}
fwrite(STDERR, "An error occurred.\n");

外部プログラムを実行

function execProgram($cmd)
{
   unset($arr);
   exec($cmd, $arr, $res);
   if ($res === 0) return $arr;
   
   return false;
}

非同期実行

exec('nohup filename > /dev/null &');

ユーザーが入力したデータを渡すことを許可する場合、escapeshellarg() または escapeshellcmd() を適用する

フォルダ以下PHP構文チェック

find . -type f -name "*.php" -exec php -l {} \;

メール送信

$headers = sprintf("From: %s\n", trim($from));
mb_language('Japanese');
mb_internal_encoding("UTF-8");
mb_send_mail(trim($to), trim($title), $body, $headers);

バッチで Notice でも停止

function strict_error_handler($errno, $errstr, $errfile, $errline)
{
    die ("STRICT: {$errno} {$errstr} {$errfile} {$errline} ".PHP_EOL);
}
set_error_handler("strict_error_handler");

引数チェック&YAMLファイル読み込み

if (count($argv) !== 2) {
    echo 'ARGUMENT ERROR';
    exit;
}
$config = yaml_parse_file('config/'.$argv[1].'.yml');
if ($config === false) echo 'yaml_parse_file '.__LINE__;

画像ビーコン(透明GIF 1x1)

$beaconGifData = pack('H*', '47494638396101000100800000ffffff00000021f90401000000002c00000000010001000002024401003b');

header('Content-Length: '.strlen($beaconGifData));
header('Content-Type: image/gif');
echo $beaconGifData;

ヒアドキュメント

print <<< EOF
   文字 文字 {$purpose} 文字 文字 文字 
EOF;
$string = <<< EOF
   文字 文字 {$purpose} 文字 文字 文字 
EOF;

BASIC認証のユーザー名取得

$_SERVER['PHP_AUTH_USER']

20バイトのランダム文字生成

substr(md5(uniqid(mt_rand(), true), 0, 20)

8桁のランダム数字生成

sprintf('%08d', mt_rand()%100000000)

ランダム文字列生成

/**
 * ランダムな文字列の生成
 */
function generateRandomString($len = 16)
{
    $res = '';
    $chars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    // $chars = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
    // $chars = '234578ABDEFGHJLMNPRTUYadefghmnprtuy'; // 見栄え発音
    // $chars = '2345789ABDEFGHJLMNPQRTUYadefghmnprtuy'; // 見栄え
    for ($i = 0; $i < $len; $i++) {
        $res .= $chars[mt_rand(0, strlen($chars)-1)];
    }
    
    return $res;
}

オートローダー関数

$h = new Hoge();

 ↓ 自動でrequire_onceが実行

require_once(DIR.'/classes/Hoge.php');

// Autoloader 
function __autoload($className)
{
   $autoloadPath = array(
       __DIR__.'/classes',
   );
   foreach ($autoloadPath as $path) {
       if (file_exists($path.'/'.$className.'.php')) {
           require_once($path.'/'.$className.'.php');
           return;
       }
   }
}

md5 を 0〜15 の数字へ変換

$code = substr(md5($str), 0, 1);
if (preg_match('@^[0-9]$@', $code)) {
    $code = (int)$code;
} else {
    // HASH を 0〜15 のグループ変換
    $code = (int)(ord($code) - 87);
}

処理時間の計測

$startTime = microtime(true);
// ほげほげ処理
$endTime = microtime(true);
echo ($endTime - $startTime)." sec\n";

外部プログラム実行

function execProgram($cmd)
{
    exec($cmd, $arr, $res);
    if ($res === 0) return $arr;
    
    return false;
}

curl リクエスト関数

function requestWeb($url, $timeout)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_AUTOREFERER, true);
    
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_MAXREDIRS, 5); // リダイレクト回数
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // SSL証明書確認無効

    $response = curl_exec($ch);
    $info = curl_getinfo($ch);
    if(curl_errno($ch)) {
        curl_close($ch);
        return [(int)($info['http_code']), $info['url'], '']; // timeout: code = 0
    }

    $header = curl_getinfo($ch);
    $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
    $body = substr($response, $headerSize);
    curl_close($ch);

    return [(int)($header['http_code']), $info['url'], $body];
}

file_get_contents で POST

/**
 * HTTPリクエスト
 */
function execRequest123($url, $body, $appConfig)
{
   $headers = ['Content-type: application/json'];
   if (! empty($appConfig['headers'])) {
       $headers = array_merge($headers, $appConfig['headers']);
   }

   if (empty($body)) {
       $method = 'GET';
       $body = '';
   } else {
       $method = 'POST';
   }
   $opts['http'] = [
     'method' => $method,
     'header' => implode("\r\n", $headers),
     'content' => $body,
   ];
   $context = stream_context_create($opts);

   return file_get_contents($url, false, $context);
}

ロック処理

$lockFile = '/tmp/deploy_lock.txt';

// ロック
if (! @symlink(__FILE__, $lockFile)) {
    echo 'Locked: '.$lockFile.PHP_EOL;
    exit(0);
}

// 排他実行プログラム

unlink($lockFile); // ロック解除
exit(0);

実行中からバックグラウンド

# Ctrl+Zで中断
bg 1
jobs 1
disown %1

アンカー要素を上にずらす

<h1> <span id=“no1” class=“anchorlink"></span>タイトル</h1>
アンカーをつけたい場所にspanで挿入。
@media only screen and (min-width:760px){
    span.anchorlink {
       position: relative;
       top: -120px;
       display: block;
    }
}

IteratorAggregate

// @see http://hensa40.cutegirl.jp/archives/2307
class ClassName implements \ArrayAccess, \IteratorAggregate
{
    // ArrayAccess
    public function offsetSet($key, $value)
    {
        $this->set($key, $value);
    }
    public function offsetExists($key)
    {
        return $this->has($key);
    }
    public function offsetGet($key)
    {
        return $this->get($key);
    }
    public function offsetUnset($key)
    {
        $this->delete($key);
    }

    // IteratorAggregate
    public function getIterator()
    {
        return new \ArrayIterator($this->data);
    }
}
class HOGE implements \ArrayAccess
{
    //オフセットが存在するかどうか
    public function offsetExists($offset) {
        return property_exists(get_called_class(), $offset);
    }
    //オフセットを取得する
    public function offsetGet($offset) {
        if ($this->offsetExists($offset)) {
            return $this->$offset;
        }
        return null;
    }
    //オフセットを設定する
    public function offsetSet($offset, $value) {
        $this->$offset = $value;
    }
    //オフセットの設定を解除する
    public function offsetUnset($offset) {
        $this->$offset = null;
    }
}

多重起動防止 crontabワンライナー

* * * * * /usr/sbin/pidof -x job.sh >/dev/null || /path/to/your/job.sh