Archives by date

You are browsing the site archives by date.

【CakePHP2.4】SessionComponent::setFlash()でエレメントを使う。

めも。

まず「app/View/Elements/」以下に好きなエレメントを作成する。

そしてコントローラーでsetFlash()をする際、下記のようにエレメントを呼び出す。

// elementFileName.ctpというエレメントを作成したと仮定する
$this->Session->setFlash('メッセージ', 'elementFileName');

これで作成したエレメントが呼び出される。

エレメント内では$messageに第一引数で渡したメッセージが格納されているので、任意の場所でechoしてやればよい。

 

【PHP】配列で二つの値が共に重複しない新規配列を返す関数。

必要だったので自作。

例えば以下のような配列があったとする。

$array = array(
	0 => array(
		'name' => 'tarou',
		'age' => '17',
	),
	1 => array(
		'name' => 'tarou',
		'age' => '19',
	),
	2 => array(
		'name' => 'tarou',
		'age' => '17',
	),
	3 => array(
		'name' => 'hanako',
		'age' => '15',
	),
	4 => array(
		'name' => 'alice',
		'age' => '20',
	),
	5 => array(
		'name' => 'bob',
		'age' => '17',
	),
	6 => array(
		'name' => 'alice',
		'age' => '20',
	),
	7 => array(
		'name' => 'bob',
		'age' => '17',
	),
	8 => array(
		'name' => 'tarou',
		'age' => '19',
	),
);

ここで配列中のnameとageが共に重複するレコードのみ取り除きたい。
というわけで下記関数を用意。

function array_pair_unique($array, $one, $two) {
	$first = reset($array);
	if(!array_key_exists($one, $first) || !array_key_exists($two, $first) ) {
		return $array;
	}
	$tmp = array();
	$return = array();
	foreach($array as $k => $v) {
		if(array_key_exists($v[$one], $tmp) && in_array($v[$two], $tmp[$v[$one]])) {
			continue;		
		} else {
			$tmp[$v[$one]][] = $v[$two];
			$return[$k] = $v;
		}
	}
	return $return;
}

上記関数を以下のようにして用いる。

$array = array_pair_unique($array, 'name', 'age');

すると下記のような結果が返る。見事に思惑通り。

array(5) {
  [0]=>
  array(2) {
    ["name"]=>
    string(5) "tarou"
    ["age"]=>
    string(2) "17"
  }
  [1]=>
  array(2) {
    ["name"]=>
    string(5) "tarou"
    ["age"]=>
    string(2) "19"
  }
  [3]=>
  array(2) {
    ["name"]=>
    string(6) "hanako"
    ["age"]=>
    string(2) "15"
  }
  [4]=>
  array(2) {
    ["name"]=>
    string(5) "alice"
    ["age"]=>
    string(2) "20"
  }
  [5]=>
  array(2) {
    ["name"]=>
    string(3) "bob"
    ["age"]=>
    string(2) "17"
  }
}

元配列のキーは保持され、重複したレコードがある場合はその中で一番最初にヒットしたもがセットされる。
関数を呼び出す際に存在しないキー名が渡された場合、元配列をそのまま返却するようにしてみた。

 

【Javascript】CSSセレクタとマッチする要素を取得する。

めも。

document.querySelector()メソッドを用いる。

例えば下記のようなHTML要素を取得したい場合。

<div class="target">Click</div>
<span id="target">Click</span>

下記の通り、CSSと同様のセレクタを渡すことで要素を選択してくれる。

var div = document.querySelector('.target');
var span = document.querySelector('#target');
console.log(div, span);

document.querySelector()は同一の要素が複数ある場合、最初にマッチした要素のみを返却する。
そのため下記のように一致する要素全てを取得したい場合はdocument.querySelectorAll()を用いるとよい。

■HTML

<div class="target">Click1</div>
<div class="target">Click2</div>
<div class="target">Click3</div>
<div class="target">Click4</div>
<div class="target">Click5</div>
<div class="target">Click6</div>

■Javascript

var divs = document.querySelectorAll('.target');
console.log(divs);

 

【Javascript】キャプチャリングとバブリングの違い。

DOMのイベントには、キャプチャリングフェーズとバブリングフェーズという二つのフェーズが存在しており、addEventListenerの第三引数にて指定することが出来る。trueを渡すと、キャプチャリングフェーズ、falseを渡すとバブリングフェーズで実行される。デフォルトはバブリングフェーズで実行される。

■実行順
キャプチャリングフェーズ => バブリングフェーズ

■キャプチャリングフェーズ
DOMの外側からイベントが発生した要素に向かってイベントが伝わる。

■バブリングフェーズ
イベントが発生した要素から外側に向かってイベントが伝わる。

 

挙動に関しては要実験。

 

【Javascript】特定の要素に対して複数のイベントを設定する。

めも。

例えば以下のようなボタン要素に対して複数のイベントをバインドさせたい場合。

<button class="js_btn">Click</button>

下記の様にaddEventListener()メソッドを用いることで同時に複数のメソッドを結びつけることができる。

<script>
var obj = {};

obj.hoge = function(){
	alert('hoge');
}

obj.piyo = function(){
	alert('piyo');
}

obj.fuga = function(){
	alert('fuga');
}


$(function(){
	var btn = document.querySelector('.js_btn');
	btn.addEventListener('click', obj.hoge, false);
	btn.addEventListener('click', obj.piyo, false);
	btn.addEventListener('click', obj.fuga, false);
})
</script>

 

【PHP】anitube保存用クラスを作った。

動画保存系クラスシリーズ。主に自分用。

/**
 * anitube保存用クラス
 * 
 * $ani = new anitubeDownloader();
 * $ani->saveDir = '動画保存用ディレクトリ'; // 末端DS無し
 * $ani->tmpDir = '作業用ディレクトリ'; // 末端DS無し
 * $ani->prefix = 'ANITUBE_'; // 動画保存時のファイル名先頭プレフィックス文字列
 * $res = $ani->download('*******');
 * 
 * @author Owner
 *
 */
class anitubeDownloader {
	
	// 動画保存ディレクトリ(末端スラッシュ無し)
	public $saveDir = '';
	
	// 一時作業ディレクトリ(末端スラッシュ無し)
	public $tmpDir = '';
	
	// 動画保存時のファイル名に付与するプレフィックス文字列
	public $prefix = 'ani_';
	
	// VIDEO_ID(http://www.anitube.se/video/*******/...)
	protected $vid = '';
	
	// エラーメッセージの入れ物
	protected $errors = array();
	
	// 動画閲覧ページのベースURL
	protected $watchBaseUrl = 'http://www.anitube.se/video/';
	
	// 動画情報を取得するためのAPIURL
	protected $apiUrl = 'http://www.anitube.se/nuevo/config.php?key=';
	
	// APIURLを括るデリミタ文字(ユニークキーを含む)
	protected $delimiter = "'";
	
	// HD動画の取得に成功したか否か
	protected $isHd = false;
	
	/**
	 * ダウンロードの実行
	 * @param string $mid
	 * @return multitype:|boolean
	 */
	public function download($vid = null) {
		$this->vid = $vid;
		$this->checkParams();
		if(!empty($this->errors)) return $this->errors;
		$tmpWorkPath = $this->tmpDir . DIRECTORY_SEPARATOR . md5(microtime());
		$headerFilePath = $this->tmpDir . DIRECTORY_SEPARATOR . 'h_' . $this->vid;
		self::tp($tmpWorkPath);
		self::tp($headerFilePath);
		
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($ch, CURLOPT_URL, $this->watchBaseUrl.$this->vid);
		$output = curl_exec($ch);
		
		// 動画のユニークキーストリングを取得する
		$keyStr = $this->getKeyStr($output);
		
		// 上記で取得したキーを元にAPIを叩き、XMLを取得
		$xml = @simplexml_load_string(file_get_contents($this->apiUrl.$keyStr));
		if(!$xml) return false;
		
		// 取得する動画サイズを決定する。(hd or normal)
		$videoUrl = $this->getVideoUrl($xml);
		
		curl_setopt($ch, CURLOPT_URL, $videoUrl);		
		// 動画DLセクション
		$execResult = false;
		$hdFp = fopen($headerFilePath, 'w');
		if($hdFp) {
			curl_setopt($ch, CURLOPT_WRITEHEADER, $hdFp);
			$twFp = fopen($tmpWorkPath, 'wb');
			if($twFp) {
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
				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);
		// HDファイルのダウンロードに成功していた場合、ファイル名のプレフィックスにhd_を追加
		if($this->isHd) $this->prefix .= 'hd_';
		// 実ファイルのパスを指定(お名前指定)
		$filePath = $this->saveDir . DIRECTORY_SEPARATOR . $this->prefix . $this->vid . $ext;
		// ファイル移動
		rename($tmpWorkPath, $filePath);
		
		// 作業ファイル削除
		self::u($tmpWorkPath);
		self::u($headerFilePath);
		return true;
		
		if(false) {
			exception:
			self::u($tmpWorkPath);
			self::u($headerFilePath);
			curl_close($ch);
			return 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.');
		if(!self::validateVid($this->vid)) array_push($this->errors, 'VID must be numeric.');
	}
	
	/**
	 * 動画IDの正規バリデーション
	 * @param unknown $check
	 * @return boolean
	 */
	protected function validateVid($check) {
		$regexNumOnly = '/^[0-9]+$/';
		if (preg_match($regexNumOnly, $check)) return true;
		return false;
	}
	
	/**
	 * HTMLファイルをパースして動画のユニークキーを取得
	 * @param unknown $html
	 * @return Ambigous <NULL, string>
	 */
	protected function getKeyStr($html) {
		$key = null;
		$apiPos = stripos($html, $this->apiUrl);
		if($apiPos) {
			$st = $apiPos + strlen($this->apiUrl);
			$ed = strpos($html, $this->delimiter, $st);
			$l = $ed - $st;
			$key = substr($html, $st, $l);
		}
		return $key;
	}
	
	protected function getVideoUrl($xml) {
		if(!empty($xml->filehd)) {
			$this->isHd = true;
			return (string)$xml->filehd;
		}
		if(!empty($xml->file)) return (string)$xml->file;
		return false;
	}
	
	/**
	 * ヘッダーファイルの情報を解析
	 * @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/3gpp':
				$e = '.3gp';
				return $e;
			case 'video/mp4':
				$e = '.mp4';
				return $e;
			case 'video/x-flv':
			default:
				$e = '.flv';
				return $e;
		}
	}
}

例の如く使い方はクラス上部のコメントアウト参照。
といいつつ一応使い方メモ。

$ani = new anitubeDownloader();
$ani->saveDir = '/path/to/saveDir';
$ani->tmpDir = '/path/to/tmpDir';
$res = $ani->download('*****');
var_dump($res);

ニコ動にしろYoutubeにしろ、これ系のクラスはやってること少しずつしか変わらないからスクレイピングの部分だけ分岐させればよいかも?

 

【jQuery】ajax()関数のラッパーメソッドまとめ。

メモ。

// GETメソッドで通信を行う。送信データ、成功時コールバック関数、レスポンスデータタイプを指定可能
get(url, data, success, dataType);

// POSTメソッドで通信を行う。引数は上記get()と同様
post(url, data, success, dataType);

// JSONデータをGETメソッドで取得する。送信データ、成功時コールバック関数を指定可能
getJSON(url, data, success);

// JavascriptをGETメソッドで取得し、実行する。成功時のコールバック関数を指定可能
getScript(url, success);

ajaxSetup()関数を用いるとajax()関数で指定可能なオプションの初期値をセットできる。
ajaxSetup()でイニシャライズを済ませておき、実際の通信はラッパー関数で行えば非常にスマートなコードにできそう。

 

【Javascript】windowオブジェクトまとめ。

めも。

// 言語情報やユーザーエージェントなどの情報が格納されている
window.navigator

// URLに関する情報が格納されている
window.location

// ブラウザ履歴を戻るback()メソッドや進めるforward()メソッドなど、履歴操作に纏わるメソッドが格納されている
window.history

// 表示している画面の幅、高さ、色数などの情報が格納されている
window.screen

// 画面内に複数のフレームが存在している場合、そのフレームへの参照が格納される
window.frames

// レンダリングされているDOMが全て格納される
window.document

たとえば。

> window.navigator.userAgent
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36"

> window.screen.width
1440

といった具合に情報を取り出すことが出来る。