Archives by date

You are browsing the site archives by date.

【MySQL】テーブル内の同一値をグルーピングして数える方法。

超絶自分用めも。

「COUNT()」関数と「GROUP BY」句を使う。

SELECT *, COUNT(site_id) FROM items GROUP BY site_id

わかりやすく書くと下記のような感じ。

SELECT [欲しいカラム名],[欲しいカラム名],[欲しいカラム名], ...,  COUNT([数えたいカラム名]) FROM [テーブル名] GROUP BY [数えたいカラム名]

結局わかりずらい。笑
まぁ自分用備忘録として。

 

【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"
}

ひじょーに便利。