cURLでクロスドメイン

サーバー1からサーバー2へ、cURLを用いクロスドメイン通信を行う。
これだけだとセキュリティ的に弱いので認証キーを組み込んだ方が良い。

サーバー1


	// 外部URL
	$url = "http://amaraimusi.sakura.ne.jp/sample/php/curl/curl_post/serv_side_cros.php";
	
	// POSTデータの作成
	$data=['id'=>1303,'animal_name'=>'tiger'];
	$json = json_encode($data);
	$data2 = ['key1'=>$json];
	$query = http_build_query($data2);
	
	$curl = curl_init(); // cURLのセッション開始およびcURLハンドラの取得
	
	// 設定
	curl_setopt_array($curl, [
			CURLOPT_URL=>$url,
			CURLOPT_RETURNTRANSFER=>true,
			CURLOPT_POST=>true,
			CURLOPT_POSTFIELDS=>$query,
			CURLOPT_FOLLOWLOCATION=>true,
	]);
	
	$html =  curl_exec($curl); // 外部URLからコンテンツを取得する。
	curl_close($curl); // cURLのセッションを終了させる。
	
	echo $html;
	

サーバー2

http://amaraimusi.sakura.ne.jp/sample/php/curl/curl_post/serv_side_cros.php

	// クロスドメイン通信の許可(cURLの場合、省略しても動くようである。)
	header('Access-Control-Allow-Origin: *');
	header('Access-Control-Allow-Methods: POST');
	
	
	// デフォルトデータにPOSTで受け取ったデータをマージする。
	$data = ['id'=>1,'animal_name'=>'dog','delete_flg'=>true];
	
	if(!empty($_POST['key1'])){
		$json=$_POST['key1'];
		$data2=json_decode($json,true);//JSONデコード
		$data = array_merge($data, $data2);
	}
	
	// データをJSON化して、レスポンスする。
	$json = json_encode($data);
	echo $json;
	

出力


	{"id":1303,"animal_name":"tiger","delete_flg":true}
	

POSTとPUTの違い

URIの決定権がサーバーにあるときPOSTである。
URIの決定権がクライアントにあるときPUT。
ほんどの場合、サーバーがURIを決めるのでPOSTがよく使われる。

CakePHPはURLの一部をデータ扱いできるため、PUTを実現するのが容易である。


ヘッダー情報を取得 | getallheaders

ヘッダー情報を取得を取得するにはgetallheaders関数を使う。


	$headers = getallheaders();//ヘッダー情報を取得する
	var_dump($headers);
	

出力

	array (size=4)
	  'Host' => string 'localhost' (length=9)
	  'Accept' => string '*/*' (length=3)
	  'Content-Length' => string '146' (length=3)
	  'Content-Type' => string 'application/x-www-form-urlencoded' (length=33)
	


cURLによるクロスドメイン:認証キー付き

サーバー1とサーバー2の間でクロスドメイン通信を行います。
公開鍵暗号による認証処理を組み込んでいます。

サーバー1


	<?php 

	// 外部URL
	$url = "http://amaraimusi.sakura.ne.jp/sample/php/curl/curl_auth/server2.php";
	
	// 認証キー
	$hash="08977553564800ea12cf5277006e238577b2318850e201b353204e9486364788";

	// POSTデータのサンプル
	$data=['id'=>1401,'animal_name'=>'unagi','hash'=>$hash];
	$json = json_encode($data);
	$data2 = ['key1'=>$json];
	$query = http_build_query($data2);
	
	$curl = curl_init(); // cURLのセッション開始およびcURLハンドラの取得
	
	// 設定
	curl_setopt_array($curl, [
			CURLOPT_URL=>$url,
			CURLOPT_RETURNTRANSFER=>true,
			CURLOPT_POST=>true,
			CURLOPT_POSTFIELDS=>$query,
			CURLOPT_FOLLOWLOCATION=>true,
	]);
	
	$html =  curl_exec($curl); // 外部URLからコンテンツを取得する。
	curl_close($curl); // cURLのセッションを終了させる。
	
	echo $html;
	?>
	

サーバー2

http://amaraimusi.sakura.ne.jp/sample/php/curl/curl_auth/server2.php

	<?php 
	// クロスドメイン通信の許可(cURLの場合、許可しなくても通信できる)
	// header('Access-Control-Allow-Origin: *');
	// header('Access-Control-Allow-Methods: POST');
	
	// デフォルトデータ
	$data = ['id'=>1400,'animal_name'=>'kame','age'=>20];
	
	if(!empty($_POST['key1'])){
		
		// データにPOSTデータをマージします。
		$json=$_POST['key1'];
		$data2=json_decode($json,true);
		$data = array_merge($data, $data2);
		
		// 認証処理
		$secretKey="xxx123";// 秘密キー(便宜上、ソースコードに直接記述しているが、本来ならDBなどから取得すべき)
		$ip=$_SERVER['REMOTE_ADDR'];// 遷移元のIPアドレスを取得する
		$hash = hash('sha256',MD5($ip.$secretKey));// IPアドレスと秘密キーから認証キーを作成する。
		
		kenshoOutput($hash);// 検証出力 ■■■□□□■■■□□□■■■□□□テスト用
		
		if($hash != $data['hash']){// POSTの認証キーと、上記で作成した認証キーを比較判定する。
			echo "Fail Auth";//認証失敗
			die();
		}
	
	}else{
		echo "NONE";
		die();
	}
	
	// データをレスポンス可してレスポンスします。
	$json = json_encode($data);//JSONエンコード
	echo $json;
	
	// 検証出力
	function kenshoOutput($hash){
		echo '認証キー:'.$hash.'<br>';
		echo 'IPアドレス【SERVER_ADDR】:'.$_SERVER['SERVER_ADDR'].'<br>';
		echo 'IPアドレス【REMOTE_ADDR】:'.$_SERVER['REMOTE_ADDR'].'<br>';
		echo 'リファラ:'.$_SERVER['HTTP_REFERER'].'<br>';
		echo 'ユーザーエージェント:'.$_SERVER['HTTP_USER_AGENT'].'<br>';
		//var_dump($_SERVER);
	}
	?>
	

出力


	認証キー:08977553564800ea12cf5277006e238577b2318850e201b353204e9486364788
	IPアドレス【SERVER_ADDR】:59.106.171.54
	IPアドレス【REMOTE_ADDR】:59.106.171.54
	リファラ:
	ユーザーエージェント:
	{"id":1401,"animal_name":"unagi","age":20,"hash":"08977553564800ea12cf5277006e238577b2318850e201b353204e9486364788"}
	


file_get_contents関数でスクレイピング

file_get_contents関数によりスクレイピングが可能になる。
スクレイピングとは、ドメインが異なる外部サイトからコンテンツを取得する方法である。


ソースコード


	<?php
	$opts = array(
		'http'=>array(
			'method'=>"GET",
			'header'=>"Accept-language: en¥r¥n" .
			"Cookie: foo=bar¥r¥n"
	  )
	);
	
	echo file_get_contents('http://www.example.com', false, stream_context_create($opts));
	?>
	
	



file_get_contentsによるクロスドメイン

file_get_contents関数は、スクレイピング(外部サイトのコンテンツを取得)で よく使われる。
file_get_contents はコンテンツを取得するだけでなく、POSTデータを送信してレスポンスを受け取ることができる。 つまりクロスドメインが可能である。

ソースコード


	<?php
	
	// POSTデータとクエリ化
	$data = array(
	    "animal_name" => "dog",
	    "animal_value" => "101"
	);
	$data = http_build_query($data, "", "&");
	
	// HTTPヘッダー情報
	$header = array(
	    "Content-Type: application/x-www-form-urlencoded",
	    "Content-Length: ".strlen($data)
	);
	
	// HTTPリクエストの設定
	$option = array(
	    "http" => array(
	        "method"  => "POST",
	        "header"  => implode("¥r¥n", $header),
	        "content" => $data
	    )
	);
	
	// クロスドメイン通信先
	$url = "http://amaraimusi.sakura.ne.jp/sample/php/cross_domain/file_get_contents/test_serv_side.php";
	
	// クロスドメイン通信
	$res = file_get_contents($url, false, stream_context_create($option));
	
	echo $res;
	?>
	

サーバーサイド

http://amaraimusi.sakura.ne.jp/sample/php/cross_domain/file_get_contents/test_serv_side.php

	<?php 
	// クロスドメイン通信の許可は不要
	// header('Access-Control-Allow-Origin: *');
	// header('Access-Control-Allow-Methods: POST');
	
	if(!empty($_POST)){
		echo 'POSTデータを受け取りました。<br>';
		foreach($_POST as $key => $val){
			echo $key.' = ' . $val . '<br>';
		}
	
	}else{
		echo 'NONE';
	}
	?>	
	

出力

	POSTデータを受け取りました。
	animal_name = dog
	animal_value = 101
	

参考サイト



クロスドメイン・ファイルアップロード

HTML
	<input id="file1" type="file" name="file1" />
	<input type="button"  value="アップロード" onclick="upload1()" />
	<div id="res"></div>
	<div id="err"></div>
	<div>
		<img id="img1" src="http://amaraimusi.sakura.ne.jp/sample/js/a009/cors_file_upload/xxx/test.jpg" alt="" />
	</div>
	

JavaScript
	function upload1(){
		var fd = new FormData();
		fd.append( "fu_file1", $("#file1").prop("files")[0] );
		var ajax_url = "http://amaraimusi.sakura.ne.jp/sample/js/a009/cors_file_upload/upload.php";
	
		$.ajax({
			type: "POST",
			url: ajax_url,
			data: fd,
			cache: false,
			dataType: "text",
			processData : false,
			contentType : false,
	
		})
		.done(function(str_json, type) {
			var data;
			try{
				data = JSON.parse(str_json);
				
			}catch(e){
				console.log(str_json);
				jQuery('#err').html(str_json);
				throw e;
			}
			
			console.log(data);
			$('#res').html('success');
			var fp = "http://amaraimusi.sakura.ne.jp/sample/js/a009/cors_file_upload/" + data.fp + '?v=' +  Date.now();
			console.log('fp=' + fp);
			$('#img1').attr('src', fp)
			
		})
		.fail(function(jqXHR, statusText, errorThrown) {
			var err_res = jqXHR.responseText;
			console.log(err_res);
			jQuery('#err').html(err_res);
			alert(statusText);
			
		});
	}	
	

サーバー側
	header('Access-Control-Allow-Origin: *');
	header('Access-Control-Allow-Methods: POST');
	
	if($_SERVER["REMOTE_ADDR"] != '126.219.137.211'){
		echo 'IPアドレス制限がかけられています。';
		die();
	}
	
	$fn = "test.jpg";
	$fp = 'xxx/' . $fn;
	
	// 一時ファイルをコピー
	move_uploaded_file($_FILES["fu_file1"]["tmp_name"], $fp);
	
	$res = ['fp'=> $fp];
	$json_str = json_encode($res, JSON_HEX_TAG | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_HEX_APOS);
	echo $json_str;
	



2次元配列データからHTMLテーブルを作成

データからHTMLのtable要素を作成する。
データはエンティティの配列である。


	/**
	 * 2次元配列データからHTMLテーブルを作成
	 * 
	 * @param array $data 2次元配列データ
	 * @param array $clms 列名リスト(省略可能。省略した場合はキーがフィールド名になる)
	 * @return string HTMLテーブルのソースコード
	 */
	public function createHtmlTable($data,$clms=array()){
		
		if(empty($data)){
			return "";
		}
		
		$ary = array();
		$ary[] = "<table class='table'>";
		
		//列を作成する
		$ary[] = "<thead><tr>";
		$fields = array_keys($data[0]);
		foreach($fields as $i => $field){
			
			// デフォルトの列名
			$clmName = $field;
			
			// 列名リストに列名があるなら、こちらを列名として取得する
			if(!empty($clms[$i])){
				$clmName = $clms[$i];
			}elseif(!empty($clms[$field])){
				$clmName = $clms[$field];
			}
			$ary[] = "<th>{$clmName}</th>";
		}
		$ary[] = "</tr></thead>";
		
		
		
		
		//データ部分を作成する
		$ary[] = "<tbody>";
		foreach($data as $i=>$ent){
			$ary[] = "<tr>";
			foreach($ent as $v){
				$ary[] = "<td>{$v}</td>";
			}
			
			$ary[] = "</tr>";
		}
		$ary[] = "</tbody>";
		

		$ary[] = "</table>";
		
		
		$code = join("¥n",$ary);
		
		return $code;
		
	}
	


フルパスを取得 (Windows環境でのパス)

	
	echo __FILE__;
	echo dirname(__FILE__);
	echo basename(__FILE__);
	

出力

	C:\xampp\htdocs\example\note10.php
	C:\xampp\htdocs\example
	note10.php
	



ディレクトリパスの種類ごとのmkdirを検証

検証結果
	mkdir('test1');
当phpファイルと同じディレクトリにtest1ディレクトリが作成される。
	mkdir('/test2');
ローカル環境(Windows)である場合、Cドライブにtest2ディレクトリが作成される。

	$doc_root = $_SERVER['DOCUMENT_ROOT'];
	mkdir($doc_root.'/test3');
			
htdocsディレクトリにtest3ディレクトリが作成される。
「C:\xampp\htdocs\test3」 (ローカル環境)
	
	$dp = dirname(__FILE__);;
	mkdir($dp.'/test4');
当phpファイルと同じディレクトリにtest1ディレクトリが作成される。
「mkdir('test4');」と同じ


フルパスのディレクトリセパレータ定数 DIRECTORY_SEPARATOR

フルパスのセパレータはOSごとにスラッシュ,バックスラッシュ,「¥」と微妙な違いがある。
DIRECTORY_SEPARATORは、OSごとのセパレータ違いを吸収できる。
短く、「DS]として定義しなおすと使いやすくなる。


	define('DS', DIRECTORY_SEPARATOR);
	$fp = dirname(__FILE__).DS.'sample'.DS.'NekoController.php';
	

出力


	C:\xampp\htdocs\xample\NekoController.php 
	



ファイルおよびフォルダの更新日時を取得する

filemtime関数でファイル、フォルダの更新日時を取得できる。

スラッシュで区切ったパスだけでなく、バックスラッシュで区切ったフルパスから更新日時を取得できる。
なお、フルパスで指定する場合、フルパスをShift-JISに変換しなければならない。

日本語ファイル名の更新日時を取得する場合、ファイル名をShift-JISに変換する必要がある。
日本語フォルダ名でも同じ。

ソースコード

	$fn = 'C:\xampp\htdocs\livipage_js\README.md';

	// 日本語のフォルダ名、ファイル名に対応。ついでにフルパスのバックスラッシュを日本語に対応。
	$fn=mb_convert_encoding($fn,'SJIS','UTF-8');

	// フォーマット変換しながら更新日時を取得
	$dt = date("Y-m-d H:i:s", filemtime($fn));
	
	echo $fn.'<br>';
	echo $dt;
	

出力
	C:/xampp/htdocs/livipage_js/README.md
	2016-08-30 10:34:59
	




絶対パスと相対パスの組み合わせたパスの記述方法


	$test = __DIR__ . '/../../test.php';
	require_once $test;