PHP

【PHP】array_unshift()の挙動まとめ。

自分用。

$ar = array('a' => 'hoge', 'b' => 'fuga', 'c' => 'piyo');
array_unshift($ar, 'test');
var_dump($ar);
↓
array(4) {
  [0]=>
  string(4) "test"
  ["a"]=>
  string(4) "hoge"
  ["b"]=>
  string(4) "fuga"
  ["c"]=>
  string(4) "piyo"
}


$ar = array('22' => 'hoge', '33' => 'fuga', '44' => 'piyo');
array_unshift($ar, 'test');
var_dump($ar);
↓
array(4) {
  [0]=>
  string(4) "test"
  [1]=>
  string(4) "hoge"
  [2]=>
  string(4) "fuga"
  [3]=>
  string(4) "piyo"
}

キーが文字列の場合情報はそのまま維持され、キーが数字の場合ゼロから振りなおされてしまう。
これは厳重注意。

 

【PHP】try、catch文使い方まとめ。

ていうかめも。自分用。

try {
	// timestampが偶数だった場合例外を投げてみる
	$isEven = false;
	if(time()%2 == 0) $isEven = true;
	if($isEven) throw new Exception('timestamp is even now.');
	
	echo '--------------------'."\n";
	echo 'start try block'."\n";
	echo '--------------------'."\n\n";
	
	echo 'timestamp is odd now.';
	
	echo "\n\n".'--------------------'."\n";
	echo 'end of try block'."\n";
	echo '--------------------'."\n";
	
} catch(Exception $e) {
	echo '--------------------'."\n";
	echo 'start catch block'."\n";
	echo '--------------------'."\n\n";
	
	echo $e->getMessage();
	
	echo "\n\n".'--------------------'."\n";
	echo 'end of catch block'."\n";
	echo '--------------------'."\n";
}

■例外が投げられなかった場合のソース

--------------------
start try block
--------------------

timestamp is odd now.

--------------------
end of try block
--------------------

■例外が投げられた場合のソース

--------------------
start catch block
--------------------

timestamp is even now.

--------------------
end of catch block
--------------------

例外が投げられるとそれ以降のtryブロックの実行は中断されcatch句により補足される。同時にExceptionオブジェクト受け取ることが出来、その例外クラスを用いた処理を記述する事が出来る。というわけ。

 

【PHP】指定した文字数で文字列を分割する。

めも。

$str = 'a very long wooooooooooooooooooooooooooooooord';
$str = wordwrap($str, 5, "<br />\n");
echo $str;

↓

a<br />
very<br />
long<br />
wooooooooooooooooooooooooooooooord

第四引数にtrueを渡すと単語の途中でもぶったぎってくれる。

$str = 'a very long wooooooooooooooooooooooooooooooord';
$str = wordwrap($str, 5, "<br />\n", true);
echo $str;

↓

a<br />
very<br />
long<br />
woooo<br />
ooooo<br />
ooooo<br />
ooooo<br />
ooooo<br />
ooooo<br />
oord

 

【PHP】 配列の全要素にユーザー関数を再帰的に適用する。

めも。

// 配列の要素を操作したい場合は$valueを参照渡ししなければならないので注意
function myCallback(&$value, $key) {
	$value .= '__';
}

$array = array('hoge', 'piyo', 'fuga', 'foo','bar','baz');
array_walk_recursive($array, 'myCallback');	
var_dump($array);

↓

array(6) {
  [0]=>
  string(6) "hoge__"
  [1]=>
  string(6) "piyo__"
  [2]=>
  string(6) "fuga__"
  [3]=>
  string(5) "foo__"
  [4]=>
  string(5) "bar__"
  [5]=>
  string(5) "baz__"
}

ちなみに上記の例ではコールバック関数をグローバルで定義したが、クラス内のfunctionを指定することも可能。
その場合array_walk_recursive()の第二引数には配列を渡す。

class hogehoge {
	
	protected function inClassCallback(&$value, $key) {
		$value .= '__inClass';
	}

	public function piyopiyo() {
		$array = array('hoge', 'piyo', 'fuga', 'foo','bar','baz');
		array_walk_recursive($array, array('self', 'inClassCallback'));	
		var_dump($array);
	}
}

↓

array(6) {
  [0]=>
  string(13) "hoge__inClass"
  [1]=>
  string(13) "piyo__inClass"
  [2]=>
  string(13) "fuga__inClass"
  [3]=>
  string(12) "foo__inClass"
  [4]=>
  string(12) "bar__inClass"
  [5]=>
  string(12) "baz__inClass"
}

自分自身のクラス内関数なので「array(‘self’, ‘functionName’)」とすればよい。
他のクラスを指定したければ「array(‘otherClassName’, ‘functionName’)」。
継承元の親クラスを指定したければ「array(‘parent’, ‘functionName’)」とかも出来たりする。

 

【PHP】Youtubeダウンロード用クラスを作ってみた。

いろんな英語のページとか参考にしながらつくってみた。

使い方はクラス上部のコメントアウトに書いたけど一番下でもっかい書く。

<?php 
/**
 * Youtube保存用クラス
 * 存在する全クオリティの動画を一括で保存する。
 * 
 * $yt = new youtubeDownloader();
 * $yt->saveDir = '動画保存用ディレクトリ'; // 末端DS無し
 * $yt->tmpDir = '作業用ディレクトリ'; // 末端DS無し
 * $yt->prefix = 'Youtube_'; // 動画保存時のファイル名先頭プレフィックス文字列
 * $res = $yt->download('*******');
 * 
 * ダウンロード成功時はtrueを返却。一つでもこけていればfalseを返却
 * download()の第二引数にtrueを渡すと保存に成功したファイル名リストを取得できる
 * ※途中でこけていればその時点までで成功しているファイル名を返却
 * 
 * @author Owner
 *
 */
class youtubeDownloader {
	
	// 動画保存ディレクトリ(末端スラッシュ無し)
	public $saveDir = '';
	
	// 一時作業ディレクトリ(末端スラッシュ無し)
	public $tmpDir = '';
	
	// 動画保存時のファイル名に付与するプレフィックス文字列
	public $prefix = 'youtube_';
	
	// video id
	protected $vid = '';
	
	// エラーメッセージの入れ物
	protected $errors = array();
	
	// ダウンロードエラー時に返却するDL済み動画リスト
	protected $successes = array();
	
	// 動画情報の取得APIURL
	protected $movieInfoApi = 'http://www.youtube.com/get_video_info?&video_id=';
	
	/**
	 * ダウンロードの実行
	 * @param string $mid
	 * @return multitype:|boolean
	 */
	public function download($vid = null, $_return = false) {
		$this->vid = $vid;
		$this->checkParams();
		if(!empty($this->errors)) return $this->errors;
		$baseTmpWorkPath = $this->tmpDir . DIRECTORY_SEPARATOR . md5(microtime());
		$baseHeaderFilePath = $this->tmpDir . DIRECTORY_SEPARATOR . 'h_' . $this->vid;
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($ch, CURLOPT_URL, $this->movieInfoApi.$this->vid);
		$output = curl_exec($ch);
		curl_close($ch);
		// 動画保存庫を割り出す
		$properties = $this->parseStr($output);
		if(empty($properties)) goto exception;
		// 動画DLセクション
		foreach($properties as $property) {
			$token = md5(microtime().mt_rand());
			$tmpWorkPath = $baseTmpWorkPath.$token;
			$headerFilePath = $baseHeaderFilePath.$token;
			// 作業ファイルの作成
			self::tp($tmpWorkPath);
			self::tp($headerFilePath);
			$ch = curl_init();
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
			curl_setopt($ch, CURLOPT_URL, $property['url']);
			$execResult = false;
			$hdFp = fopen($headerFilePath, 'w');
			if($hdFp) {
				curl_setopt($ch, CURLOPT_WRITEHEADER, $hdFp);
				$twFp = fopen($tmpWorkPath, 'wb');
				if($twFp) {
					curl_setopt($ch, CURLOPT_FILE, $twFp);
					$execResult = curl_exec($ch);
					curl_close($ch);
					fclose($twFp);
				}
				fclose($hdFp);
			}
			// エラー処理
			if(!$execResult) goto exception;
			// headerFileの解析(ファイルタイプ、拡張子を取得)
			$contentType = self::parseHeaderFile($headerFilePath);
			$ext = self::getFileExtension($contentType);
			// 実ファイルのパスを指定(お名前指定)
			$separator = '_';
			$filePath = $this->saveDir.DIRECTORY_SEPARATOR.$this->prefix.$this->vid.$separator.$property['itag'].$separator.$property['quality'].$ext;
			// ファイル移動
			rename($tmpWorkPath, $filePath);
			// DL済みリストにファイル名を追加
			array_push($this->successes, basename($filePath));
			// 作業ファイル削除
			self::u($tmpWorkPath);
			self::u($headerFilePath);
		}
		return ($_return) ? $this->successes : true;
		if(false) {
			exception:
			self::u($tmpWorkPath);
			self::u($headerFilePath);
			curl_close($ch);
			return ($_return) ? $this->successes : false;
		}	
	}
	
	/**
	 * ファイルの存在確認と作成、及び権限の付与
	 * @param unknown $path
	 */
	protected function tp($path) {
		if(!file_exists($path)) touch($path);
		chmod($path, 0777);
	}
	
	/**
	 * ファイルの存在確認と削除
	 * @param unknown $path
	 */
	protected function u($path) {
		if(file_exists($path)) unlink($path);
	}

	/**
	 * パラメーターチェック
	 */
	protected function checkParams() {
		if(!is_dir($this->saveDir)) array_push($this->errors, 'saveDir must be a directory.');
		if(fileperms($this->saveDir) != '16895') array_push($this->errors, 'saveDir required to be permission 777.');
		if(!is_dir($this->tmpDir)) array_push($this->errors, 'tmpDir must be a directory.');
		if(fileperms($this->tmpDir) != '16895') array_push($this->errors, 'tmpDir required to be permission 777.');
		if(empty($this->vid)) array_push($this->errors, 'vid is required but not set.');
	}
	
	/**
	 * APIより取得した文字列をパースして動画保存庫を特定
	 * @param unknown $html
	 * @return Ambigous <NULL, string>
	 */
	protected function parseStr($str) {
		$properties = array();
		parse_str($str);
		if(!isset($url_encoded_fmt_stream_map)) return $properties;
		$formats = explode(',',$url_encoded_fmt_stream_map);
		if(empty($formats)) return $properties;
		$exp = time();
		foreach($formats as $_key => $format) {
			parse_str($format);
			$properties[$_key]['itag'] = $itag;
			$properties[$_key]['quality'] = $quality;
			$properties[$_key]['type'] = reset(explode(';',$type));
			$decodedUrl = urldecode($url);
			parse_str($decodedUrl);
			$properties[$_key]['url'] = $decodedUrl.'&signature='.$sig;
			$properties[$_key]['expires'] = date("G:i:s T", $exp);
		}
		return $properties;		
	}
	
	/**
	 * ヘッダーファイルの情報を解析
	 * @param unknown $path
	 * @return Ambigous <NULL, string>
	 */
	protected function parseHeaderFile($path) {
		$hd = file_get_contents($path);
		$ct = null;
		$k = 'Content-Type:';
		$st = stripos($hd, $k);
		if($st !== false) {
			$st += strlen($k);
			$ed = strpos($hd, "\n", $st);
			if($ed === false) $ed = strlen($hd);
			$l = $ed - $st;
			$ct = strtolower(trim(substr($hd, $st, $l)));
		}
		return $ct;	
	}
	
	/**
	 * コンテントタイプより動画ファイルの拡張子を分析し返却
	 * @param unknown $ct
	 * @return string
	 */
	protected function getFileExtension($ct) {
		$e = '';
		switch($ct) {
			case 'video/webm':
				$e = '.webm';
				return $e;
			case 'video/3gpp':
				$e = '.3gp';
				return $e;
			case 'video/mp4':
				$e = '.mp4';
				return $e;
			case 'video/x-flv':
			default:
				$e = '.flv';
				return $e;
		}
	}
}

ということで使い方はコメントアウトの通りなんだけども、一応使用例を。

$yt = new youtubeDownloader();
$yt->saveDir = '/path/to/save/dir';
$yt->tmpDir = '/path/to/tmp/work/dir';
$res = $yt->download('XXXXXXXXXXX', true);
var_dump($res);

↓

array(8) {
  [0]=>
  string(32) "youtube_XXXXXXXXXXX_45_hd720.flv"
  [1]=>
  string(32) "youtube_XXXXXXXXXXX_22_hd720.mp4"
  [2]=>
  string(32) "youtube_XXXXXXXXXXX_44_large.flv"
  [3]=>
  string(33) "youtube_XXXXXXXXXXX_43_medium.flv"
  [4]=>
  string(33) "youtube_XXXXXXXXXXX_18_medium.mp4"
  [5]=>
  string(31) "youtube_XXXXXXXXXXX_5_small.flv"
  [6]=>
  string(32) "youtube_XXXXXXXXXXX_36_small.3gp"
  [7]=>
  string(32) "youtube_XXXXXXXXXXX_17_small.flv"
}

のような感じに値が返却され、保存が完了する。

 

【PHP】クエリストリング文字列を解析して変数を生成する。

めも。

URLの「?」以降にくっついてる文字列を引っこ抜いてパースさせる。
※最初のクエリ先頭に「?」が残ったままだと正しくパースできないので注意。

$queryStr = 'hoge=foo&fuga=bar&piyo=baz';
parse_str($queryStr);
var_dump($hoge, $fuga, $piyo);
↓
string(3) "foo"
string(3) "bar"
string(3) "baz"

パース時に[]を用いることで配列を生成することも可能。

$queryStr = 'hoge[]=foo&fuga[]=bar&piyo[]=baz';
parse_str($queryStr);
var_dump($hoge, $fuga, $piyo);
↓
array(1) {
  [0]=>
  string(3) "foo"
}
array(1) {
  [0]=>
  string(3) "bar"
}
array(1) {
  [0]=>
  string(3) "baz"
}
$queryStr = 'hoge[]=foo&hoge[]=bar&hoge[]=baz';
parse_str($queryStr);
var_dump($hoge);
↓
array(3) {
  [0]=>
  string(3) "foo"
  [1]=>
  string(3) "bar"
  [2]=>
  string(3) "baz"
}

ひじょーに便利。

 

【CakePHP2.3】Configureクラス自分用まとめ。

自分用めも。

■設定値を書き込む

// 'hoge'という名前で'value'という値を書き込む
Configure::write('hoge', 'value');

■設定値を読み込む

// 'hoge'という名前の設定値を読み込む
$conf = Configure::read('hoge');

■設定値をファイルに書き出す

// 'hoge'という名前の設定値をmyConfig.phpとして書き出す
configure::dump('myConfig', 'default', array('hoge'));

※書き出されるディレクトリは「app/Config」なのでConfigフォルダに書き込み権限を与えなければならない
※第3引数を指定しなかった場合は全ての設定値が書き出される

■書き出した設定ファイルを読み込む

// 「app/Config」下の「myConfig.php」を読み込む
Configure::load('myConfig');

// ファイルの読み込みが完了したらread()メソッドで設定値を読み込む
$conf = configure::read('hoge');

 

おまけ。

■CakePHPのバージョンを取得する

$version = Configure::version();

 

【CakePHP2.3】外部クラスをオートロード対象として宣言する。

めも。

「App::uses()」を使用し利用したいクラスを宣言すると、そのクラスを呼び出した際に自動でインポートされるようになる。

■関数定義

App::uses($className, $location);

■引数詳細

引数 初期値 詳細
$className 省略不可 クラス名
$location 省略不可 クラスの所属するパッケージ名

以下例。

App::uses('myExampleClass', 'Lib');
App::uses('myCustomClass', 'Vendor');
App::uses('mySampleClass', 'Controller/Component');

appディレクトリ下のLibやVendorフォルダに外部クラスを置いてやることで、上記のようにオートロード対象として指定してやることが可能。
App::uses()はApp::build()で指定したパスリストと本メソッドで指定したクラス名を結びつけオートロードをするためのものなので、本質的なネームスペースとは異なることに注意されたし。