Qupload + 多种素材转换成图片实现 (psd,cdr,ai,png,jpg,gif,zip,rar,ppt,pptx,pdf,mp4,avi,mov)

ppt如果直接openoffice解析成pdf再转图片,会有错位问题. 暂时只发现可以通过python调用wps实现兼容的解析.

<?php
class QuploadController extends CController {
	/**
	 * 首页
	 */
	protected function beforeAction($action) {
		return true;
	}
	function actionDemo() {
		$this->renderPartial ( 'demo' );
	}
	function log($params = []) {
		write_log ( 'qupload', $params );
	}
	function actionToken() {
		$u = lib ( 'qupload/base.php' );
		
		$file_format = preg_replace ( '/^.+\.(\w+)$/ims', '\\1', $_GET ['name'] );
		$file_format = strtolower ( $file_format );
		if (! in_array ( $file_format, [ 
				'psd',
				'cdr',
				'ai',
				'png',
				'jpg',
				'gif',
				'zip',
				'rar',
				'ppt',
				'pptx',
				'pdf',
				'mp4',
				'avi',
				'mov' 
		] )) {
			die ( 'do not allow the file format.' );
		}
		$token = $u->genToken ( $_GET ['name'], $_GET ['size'], $_GET ['time'] );
		if ($token) {
			if ($_GET ['size'] > 4 * 1000 * 1000 * 1000) {
				$result = json_encode ( array (
						'token' => $token,
						'range' => implode ( '-', $u->getRange ( $token ) ),
						'status' => $u->getStatus ( $token ),
						'tmp_dir' => $u->getInterval (),
						'status' => 2,
						'error_msg' => '文件大小不能超过1G' 
				) );
				$this->log ( $result );
				echo $result;
				exit ();
			}
			$status = $u->getStatus ( $token );
			if ($status === 2) {
				// 上传成功
				try {
					list ( $images_list, $file_info ) = $this->parseImglist ( $u->getInterval () . '/' . $token );
					$error_msg = "success";
				} catch ( Exception $e ) {
					$error_msg = $e->getMessage ();
				}
			}
			$result = json_encode ( array (
					'token' => $token,
					'range' => implode ( '-', $u->getRange ( $token ) ),
					'status' => $u->getStatus ( $token ),
					'tmp_dir' => $u->getInterval (),
					'error_msg' => $error_msg,
					'images_list' => $images_list 
			) );
			
			$this->log ( $result );
			echo $result;
			exit ();
		}
	}
	public static function uploadDir() {
		return UPLOAD_DIR . 'file/';
	}
	public static function uploadUrl() {
		return UPLOAD_URL . 'file/';
	}
	function actionDownload() {
		// 弃用,上传成功,根据文件路径,将文件重命名
		// 上传时,需要检测当前用户是否登录.
		// exit ();
		// $_REQUEST ['file'] = '17625/3344abdedd12a65dc7f78d991338d95c4558f66f';
		// $_REQUEST ['file'] = '17625/050f98d89455c3e6e4c6f96f8d1f7dc8b43b5e70';
		$file = urldecode ( $_REQUEST ['file'] );
		if (! preg_match ( '/^\/?\d+\/\w+\.\w+$/ims', $file )) {
			die ( 'the filename is error' );
		}
		$filePath = UPLOAD_DIR . 'file/' . $file;
		if (! file_exists ( $filePath )) {
			die ( 'the file do not exists' );
		}
		if (! file_exists ( $filePath . '.json' )) {
			die ( 'the file json do not exists' );
		}
		$file_info = file_get_contents ( $filePath . '.json' );
		$file_info = json_decode ( $file_info, true );
		if (! $file_info) {
			die ( 'the file json is error' );
		}
		$filename = $file_info ['name'];
		header ( 'Content-Description: File Transfer' );
		header ( 'Content-Type: application/octet-stream' );
		$ua = $_SERVER ["HTTP_USER_AGENT"];
		if (preg_match ( '/MSIE/', $ua )) {
			header ( 'Content-Disposition: attachment; filename="' . rawurlencode ( $filename ) . '"' );
		} elseif (preg_match ( "/Firefox/", $ua )) {
			header ( 'Content-Disposition: attachment; filename*="utf8\'\'' . $filename . '"' );
		} else {
			header ( 'Content-Disposition: attachment; filename="' . $filename . '"' );
		}
		header ( 'Content-Transfer-Encoding: binary' );
		header ( 'Expires: 0' );
		header ( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
		header ( 'Pragma: public' );
		header ( 'Content-Length: ' . filesize ( $filePath ) );
		set_time_limit ( 300 ); // 避免下载超时
		ob_end_clean (); // 避免大文件导致超过 memory_limit 限制
		readfile ( $filePath );
	}
	public function actionFetch_img_list() {
		$u = lib ( 'qupload/base.php' );
		$token = $_REQUEST ['token'];
		$status = $u->getStatus ( $token );
		if ($status == 2) {
			$error_msg = 'success';
		} else {
			$error_msg = 'error';
		}
		$img_dir = Article::uploadFileDir () . $u->getInterval () . '/' . $token . '_img/';
		$img_list = FileTools::getCurList ( $img_dir, array (
				'/\.(png|jpg|gif)$/i' => false 
		) );
		if (empty ( $img_list )) {
			throw new Exception ( "[{$token}]获取图片异常" );
		}
		// ppt 解析出所有图片
		$images_list = [ ];
		$dir = str_replace ( '\\', '/', WWW_DIR );
		foreach ( $img_list as $img ) {
			$img = str_replace ( '\\', '/', $img );
			$img = str_replace ( $dir, '', $img );
			$images_list [] = $img;
		}
		echo json_encode ( array (
				'token' => $token,
				'range' => implode ( '-', $u->getRange ( $token ) ),
				'status' => $status,
				'tmp_dir' => $u->getInterval (),
				'error_msg' => $error_msg,
				'images_list' => $images_list 
		) );
	}
	function actionUpload() {
		set_time_limit ( 0 );
		ignore_user_abort ( true );
		$u = lib ( 'qupload/base.php' );
		$token = isset ( $_GET ['token'] ) ? $_GET ['token'] : null;
		$start = isset ( $_GET ['start'] ) ? ( int ) $_GET ['start'] : null;
		if ($start > 4 * 1000 * 1000 * 1000) {
			$msg = 'do not allow the size more than 1GB';
			$this->log ( [ 
					$msg,
					$_GET 
			] );
			die ( $msg );
		}
		if (! $info = $u->getTokenInfo ( $token )) {
			$msg = 'can not get the token info';
			$this->log ( [ 
					$msg,
					$_GET 
			] );
			die ( $msg );
		}
		$fp = fopen ( 'php://input', 'r' );
		$size = $u->size ( $token );
		
		// 如果上传起始位置大于文件位置 肯定失败
		if ($start > $size) {
			$this->log ( [ 
					'position is wrong',
					$_GET 
			] );
			exit ( 0 );
		}
		
		// 如果文件已经存在 那么肯定是添加
		if ($size > 0) {
			$u->append ( $token, $fp, $start );
		} else {
			$u->store ( $token, $fp );
		}
		
		$status = $u->getStatus ( $token );
		$images_list = [ ];
		if ($status === 3) {
			$u->delete ( $token );
			// 上传出错,清理标记,重新上传
			$status = 0;
		} elseif ($status === 2) {
			// 上传成功
			try {
				list ( $images_list, $file_info ) = $this->parseImglist ( $u->getInterval () . '/' . $token );
				$error_msg = "success";
			} catch ( Exception $e ) {
				$error_msg = $e->getMessage ();
			}
		} else {
			// status=1
			// 上传中
		}
		$result = json_encode ( array (
				'range' => implode ( '-', $u->getRange ( $token ) ),
				'status' => $status,
				'tmp_dir' => $u->getInterval (),
				'token' => $token,
				'error_msg' => $error_msg,
				'images_list' => $images_list 
		) );
		
		$this->log ( $result );
		echo $result;
	}
	function parseImglist($upload_file) {
		$file_path = UPLOAD_DIR . '/file/' . $upload_file;
		if (! file_exists ( $file_path )) {
			sjson ( false, $title, '文件不存在' );
		}
		$file_info = file_get_contents ( $file_path . '.json' );
		$file_info = json_decode ( $file_info, true );
		if (! $file_info) {
			sjson ( false, $title, '文件信息异常' );
		}
		$file_format = preg_replace ( '/^.+\.(\w+)$/ims', '\\1', $file_info ['name'] );
		$file_format = strtolower ( $file_format );
		$file_flag = preg_replace ( '/^\w+\/(\w+\.\w+)$/ims', '\\1', $upload_file );
		$file_path_with_format = $file_path . $file_format;
		
		// 如果为图片,直接复制图片到指定目录
		$img_dir = $file_path . '_img';
		if (file_exists ( $img_dir )) {
			lib ( 'FileSystem.php' );
			// 如果目录已经存在,删除,再进行命令调用.
			// 不然容易出现是否覆盖的提示,导致脚本异常
			rmdirs ( $img_dir );
		}
		mkdir ( $img_dir, 0777 );
		// 复制文件,并做解析处理,如果防止出现异常时,需要进行二次提交
		if (in_array ( $file_format, [ 
				'psd',
				'cdr',
				'ai',
				'png',
				'jpg',
				'gif',
				'zip',
				'rar' 
		] )) {
			// 只有图片允许压缩包
			// psd,cdr,ai,png,jpg,gif,zip
			// sjson ( false, $title, '平面设计只允许上传psd,cdr,ai,png,jpg,gif,zip<br>不能' . $file_format );
			if (in_array ( $file_format, [ 
					'zip',
					'rar' 
			] )) {
				// 先获取压缩包文件内容,检查格式是否包含在指定列表中
				if ($file_format == 'zip') {
					$command = "unzip -l {$file_path}";
				} else {
					$command = "unrar v {$file_path}";
				}
				list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]压缩包内容获取异常" );
				// 不管压缩包格式是rar 还是 zip 获取内容列表时,都是
				// -----
				// ....
				// ---------
				$begin_file_content = 0;
				foreach ( $output as $line ) {
					if (stristr ( $line, '----' )) {
						$begin_file_content ++;
						continue;
					}
					if ($begin_file_content == 1) {
						$img_format = preg_replace ( '/^.+\.(\w+)$/ims', '\\1', $line );
						if (! in_array ( $img_format, [ 
								'psd',
								'cdr',
								'ai',
								'png',
								'jpg',
								'gif' 
						] )) {
							throw new Exception ( "[{$upload_file}]压缩包文件内容格式异常." );
						}
					} elseif ($begin_file_content == 2) {
						break;
					}
				}
				if ($file_format == 'zip') {
					$command = "unzip {$file_path} -d {$img_dir}";
				} else {
					$command = "unrar x {$file_path} {$img_dir}";
				}
				list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]压缩包解压异常" );
				$file_list = FileTools::getCurList ( $img_dir, array (
						'/\.(png|jpg|gif|ai|cdr|psd)$/i' => false 
				) );
				if (empty ( $file_list )) {
					throw new Exception ( "[{$upload_file}]获取压缩包内容异常" );
				}
				// ppt 解析出所有图片
				$images_list = [ ];
				foreach ( $file_list as $file ) {
					$format = preg_replace ( '/^.+\.(\w+)$/ims', '\\1', $file );
					if (in_array ( $format, [ 
							'psd',
							'ai' 
					] )) {
						// 通过命令将文件转成图片
						$command = "convert {$file}[0] {$file}.png";
						list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]图片[{$format}]转化异常" );
						$file = $file . '.png';
					} elseif (in_array ( $format, [ 
							'cdr' 
					] )) {
						// cdr 格式比较特殊
						// 通过命令将文件转成图片
						$_pdf_file = "{$file}.pdf";
						$command = "uniconvertor {$file} {$_pdf_file}";
						list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]cdr图片转化pdf异常" );
						$command = "convert {$_pdf_file} {$_pdf_file}.png";
						list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]cdr图片转化pdf解析成图片异常" );
						$file = $_pdf_file . '.png';
					}
					$images_list [] = preg_replace ( '/\/data\/www\/sucai\/www/ims', '', $file );
				}
			} elseif (in_array ( $file_format, [ 
					'png',
					'jpg',
					'gif' 
			] )) {
				// 如果为图片,直接复制图片到指定目录
				$img_path = $img_dir . '/' . $file_flag;
				copy ( $file_path, $img_path );
				$images_list [] = Article::uploadFileUrl () . $upload_file . '_img/' . $file_flag;
			} elseif (in_array ( $file_format, [ 
					'psd',
					'ai' 
			] )) {
				// 通过命令将文件转成图片
				$img_path = $upload_file . '_img/' . $file_flag . '.png';
				$command = "convert {$file_path}[0] " . Article::uploadFileDir () . $img_path;
				list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]图片转化异常" );
				$images_list [] = Article::uploadFileUrl () . $img_path;
			} elseif (in_array ( $file_format, [ 
					'cdr' 
			] )) {
				// cdr 格式比较特殊
				// 通过命令将文件转成图片
				$_pdf_file = "{$img_dir}{$file_flag}.pdf";
				$command = "uniconvertor {$file_path} {$_pdf_file}";
				list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]cdr图片转化pdf异常" );
				$command = "convert {$_pdf_file} {$_pdf_file}.png";
				list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]cdr图片转化pdf解析成图片异常" );
				$images_list [] = preg_replace ( '/\/data\/www\/sucai\/www/ims', '', "{$_pdf_file}.png" );
			}
		} elseif (in_array ( $file_format, [ 
				'ppt',
				'pptx' 
		] )) {
			$jod_dir = EXT_DIR . '/convert_tools/';
			// sjson ( false, $title, 'PPT模板只允许上传ppt,pptx' );
			$command = "convert {$file_path}[0] " . Article::uploadFileDir () . $img_path;
			$pdf_file = $img_dir . '/' . $file_flag . '.pdf';
			// ppt to pdf
			$command = "java -jar {$jod_dir}jodconverter/lib/jodconverter-cli-2.2.2.jar {$file_path} {$pdf_file}";
			list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]ppt转pdf异常" );
			// pdf to png
			$command = "convert {$pdf_file} {$pdf_file}.png";
			list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]pdf转图片异常" );
			$img_list = FileTools::getCurList ( $img_dir, array (
					'/\.png$/i' => false 
			) );
			if (empty ( $img_list )) {
				throw new Exception ( "[{$upload_file}]获取图片异常" );
			}
			// ppt 解析出所有图片
			$images_list = [ ];
			foreach ( $img_list as $img ) {
				$images_list [] = preg_replace ( '/\/data\/www\/sucai\/www/ims', '', $img );
			}
		} elseif (in_array ( $file_format, [ 
				'pdf' 
		] )) {
			// pdf to png
			$command = "convert {$file_path} {$img_dir}/{$file_flag}.png";
			list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]pdf转图片异常" );
			$img_list = FileTools::getCurList ( $img_dir, array (
					'/\.png$/i' => false 
			) );
			if (empty ( $img_list )) {
				throw new Exception ( "[{$upload_file}]获取图片异常" );
			}
			// ppt 解析出所有图片
			$images_list = [ ];
			foreach ( $img_list as $img ) {
				$images_list [] = preg_replace ( '/\/data\/www\/sucai\/www/ims', '', $img );
			}
		} else if (in_array ( $file_format, [ 
				'mp4',
				'avi',
				'mov' 
		] )) {
			// 视频 mp4,avi,mov
			// sjson ( false, $title, '视频编辑只允许上传mp4,avi,mov' );
			// 获取视频总长度
			$file_length = $this->getVideoLength ( $file_path );
			if ($file_length < 10) {
				// 视频长度小于10秒,只获取一张图片,
				$file_img_spans = [ 
						0 
				];
			} else {
				// 每10分之一长度的地方进行图片采集
				$file_img_span = intval ( $file_length / 10 );
				while ( ($left_file_length += $file_img_span) <= $file_length ) {
					$file_img_spans [] = $left_file_length;
				}
			}
			foreach ( $file_img_spans as $time_span ) {
				$img = "{$img_dir}/{$file_flag}-{$time_span}.png";
				// 小于0.1容易生成空白图片,提示缓存异常啥的
				$command = "ffmpeg -ss {$time_span} -t 0.1  -i {$file_path} -y -f mjpeg {$img}";
				list ( $val, $output, $result ) = $this->runCommand ( $command, "[{$upload_file}]视频提取图片[{$time_span}]异常" );
			}
			$img_list = FileTools::getCurList ( $img_dir, array (
					'/\.png$/i' => false 
			) );
			if (empty ( $img_list )) {
				throw new Exception ( "[{$upload_file}]获取图片异常" );
			}
			// ppt 解析出所有图片
			$images_list = [ ];
			foreach ( $img_list as $img ) {
				chmod ( $img, 0777 );
				$images_list [] = preg_replace ( '/\/data\/www\/sucai\/www/ims', '', $img );
			}
		} else {
			die ( "[{$upload_file}]上传文件格式异常" );
		}
		return [ 
				$images_list,
				$file_info 
		];
	}
	/**
	 *
	 * @param unknown $command        	
	 * @return string[ $val,//异常状态标记,通常0表示正常
	 *         $output,//命令执行结果
	 *         $result//最后一句命令返回
	 *         ]
	 */
	function runCommand($command, $msg = '') {
		$output = "";
		$val = "";
		// $val=0 表示正常
		// 需要标记 ' 2>&1' 将屏幕输出内容(包括异常)全部进行反馈,不加会返回空
		$result = exec ( $command . ' 2>&1', $output, $val );
		if ($val) {
			// gddebug ( $command, $val, $output, $result );
			write_log ( 'runcommand', [ 
					$command,
					$val,
					$output,
					$result 
			] );
			throw new Exception ( $msg );
		}
		return [ 
				$val,
				$output,
				$result 
		];
	}
	function getVideoLength($file) {
		if (! file_exists ( $file )) {
			throw new Exception ( "file do not exists" );
		}
		$dir = dirname ( $file );
		$command = "ffmpeg  -i {$file} 2>&1";
		$output = "";
		$val = "";
		$result = exec ( $command, $output, $val );
		foreach ( $output as $line ) {
			if (preg_match ( '/Duration\: (\d+(?:\.\d+)?):(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)\, start/ims', $line, $match )) {
				$second = intval ( $match [1] ) * 60 * 60 + intval ( $match [2] ) * 60 + intval ( $match [3] );
				return $second;
			}
		}
		throw new Exception ( "识别不到视频,请检查 result 变量内容" );
	}
	function actionIndex() {
		exit ();
	}
}

 

任意多边形坐标范围判断(重写自百度地图js文件)

<?php
/**
 *
 * @author Administrator
 *
 */
class MapTools {
	public static function model() {
		return new MapTools ();
	}
	
	/**
	 * 判断点是否在矩形内
	 *
	 * @param
	 *        	{Point} point 点对象
	 * @param
	 *        	{Bounds} bounds 矩形边界对象
	 * @return s {Boolean} 点在矩形内返回true,否则返回false
	 */
	function isPointInRect($point, $bounds) {
		// 西南脚点
		$sw = $bounds ['sw'];
		// 东北脚点
		$ne = $bounds ['ne'];
		return ($point ['lng'] >= $sw ['lng'] && $point ['lat'] >= $sw ['lat'] && $point ['lng'] <= $ne ['lng'] && $point ['lat'] <= $ne ['lat']);
	}
	
	/**
	 * 返回一个多边形的最小外矩形(西南点和东北点的坐标)
	 *
	 * @param array $polygon        	
	 */
	function getBoundsByPolygon($polygon) {
		$least_lng = $polygon [0] ['lng'];
		$least_lat = $polygon [0] ['lat'];
		$largest_lng = $polygon [0] ['lng'];
		$largest_lat = $polygon [0] ['lat'];
		foreach ( $polygon as $point ) {
			if ($point ['lng'] < $least_lng) {
				$least_lng = $point ['lng'];
			}
			if ($point ['lat'] < $least_lat) {
				$least_lat = $point ['lat'];
			}
			if ($point ['lng'] > $largest_lng) {
				$largest_lng = $point ['lng'];
			}
			if ($point ['lat'] > $largest_lat) {
				$largest_lat = $point ['lat'];
			}
		}
		return array (
				'sw' => array (
						'lng' => $least_lng,
						'lat' => $least_lat 
				),
				'ne' => array (
						'lng' => $largest_lng,
						'lat' => $largest_lat 
				) 
		);
	}
	
	/**
	 * 判断点是否在多边形内
	 *
	 * @param unknown_type $point        	
	 * @param unknown_type $polygon        	
	 * @return boolean
	 */
	function isPointInPolygon($point, $polygon) {
		if (empty ( $polygon )) {
			return false;
		}
		// 首先判断点是否在多边形的外包矩形内,如果在,则进一步判断,否则返回false
		// 可以通过绘制矩形时,补充多边形的中心进行计算处理.
		$polygonBounds = $this->getBoundsByPolygon ( $polygon );
		if (! $this->isPointInRect ( $point, $polygonBounds )) {
			return false;
		}
		$pts = $polygon;
		// 获取多边形点
		
		// 下述代码来源:http://paulbourke.net/geometry/insidepoly/,进行了部分修改
		// 基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则
		// 在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。
		
		$N = count ( $pts );
		$boundOrVertex = true;
		// 如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
		$intersectCount = 0;
		// cross points count of x
		$precision = 2e-10;
		// 浮点类型计算时候与0比较时候的容差
		$p1 = $p2 = null;
		// neighbour bound vertices
		$p = $point;
		// 测试点
		
		$p1 = $pts [0];
		// left vertex
		for($i = 1; $i <= $N; ++ $i) {
			// check all rays
			if ($p ['lng'] == $p1 ['lng'] && $p ['lat'] == $p1 ['lat']) {
				return $boundOrVertex;
				// p is an vertex
			}
			$p2 = $pts [$i % $N];
			// right vertex
			if ($p ['lat'] < min ( $p1 ['lat'], $p2 ['lat'] ) || $p ['lat'] > max ( $p1 ['lat'], $p2 ['lat'] )) {
				// ray
				// is
				// outside
				// of
				// our
				// interests
				$p1 = $p2;
				continue;
				// next ray left point
			}
			
			if ($p ['lat'] > min ( $p1 ['lat'], $p2 ['lat'] ) && $p ['lat'] < max ( $p1 ['lat'], $p2 ['lat'] )) {
				// ray
				// is
				// crossing
				// over
				// by
				// the
				// algorithm
				// (common
				// part
				// of)
				if ($p ['lng'] <= max ( $p1 ['lng'], $p2 ['lng'] )) {
					// x is before of
					// ray
					if ($p1 ['lat'] == $p2 ['lat'] && $p ['lng'] >= min ( $p1 ['lng'], $p2 ['lng'] )) {
						// overlies
						// on
						// a
						// horizontal
						// ray
						return $boundOrVertex;
					}
					
					if ($p1 ['lng'] == $p2 ['lng']) {
						// ray is vertical
						if ($p1 ['lng'] == $p ['lng']) {
							// overlies on a vertical ray
							return $boundOrVertex;
						} else {
							// before ray
							++ $intersectCount;
						}
					} else {
						// cross point on the left side
						$xinters = ($p ['lat'] - $p1 ['lat']) * ($p2 ['lng'] - $p1 ['lng']) / ($p2 ['lat'] - $p1 ['lat']) + $p1 ['lng'];
						// cross
						// point
						// of
						// lng
						if (abs ( $p ['lng'] - $xinters ) < $precision) {
							// overlies on
							// a ray
							return $boundOrVertex;
						}
						
						if ($p ['lng'] < $xinters) {
							// before ray
							++ $intersectCount;
						}
					}
				}
			} else { // special case when ray is crossing through the vertex
				if ($p ['lat'] == $p2 ['lat'] && $p ['lng'] <= $p2 ['lng']) {
					// p crossing
					// over
					// p2
					$p3 = $pts [($i + 1) % $N];
					// next vertex
					if ($p ['lat'] >= min ( $p1 ['lat'], $p3 ['lat'] ) && $p ['lat'] <= max ( $p1 ['lat'], $p3 ['lat'] )) {
						// p.lat
						// lies
						// between
						// p1.lat
						// &
						// p3.lat
						++ $intersectCount;
					} else {
						$intersectCount += 2;
					}
				}
			}
			$p1 = $p2;
			// next ray left point
		}
		
		if ($intersectCount % 2 == 0) {
			// 偶数在多边形外
			return false;
		} else {
			// 奇数在多边形内
			return true;
		}
	}
	/**
	 * 签名校验
	 *
	 * @param unknown $ak        	
	 * @param unknown $sk        	
	 * @param unknown $url        	
	 * @param unknown $querystring_arrays        	
	 * @param string $method        	
	 * @return string
	 */
	function caculateAKSN($ak, $sk, $url, $querystring_arrays, $method = 'GET') {
		if ($method === 'POST') {
			ksort ( $querystring_arrays );
		}
		$querystring = http_build_query ( $querystring_arrays );
		return md5 ( urlencode ( $url . '?' . $querystring . $sk ) );
	}
	/**
	 * 通过地址返回坐标
	 *
	 * @param string $address_name        	
	 * @return mixed|boolean
	 */
	function getLocationByAddress($address_name) {
		// API控制台申请得到的ak(此处ak值仅供验证参考使用)
		$ak = 'xxxxxxxxxxxxxx';
		// 应用类型为for server, 请求校验方式为sn校验方式时,系统会自动生成sk,可以在应用配置-设置中选择Security
		// Key显示进行查看(此处sk值仅供验证参考使用)
		$sk = 'xxxxxxxxxxxxxxxxx';
		// 以Geocoding服务为例,地理编码的请求url,参数待填
		$url = "http://api.map.baidu.com/geocoder/v2/?address=%s&output=%s&ak=%s&sn=%s";
		// get请求uri前缀
		$uri = '/place/v2/search';
		// 地理编码的请求output参数
		$output = 'json';
		$pois = '0';
		$query = $address_name;
		$region = '全国';
		$scope = 1;
		$querystring_arrays = array (
				'query' => $query,
				'output' => $output,
				'ak' => $ak,
				'scope' => $scope,
				'region' => $region 
		);
		$url = "http://api.map.baidu.com/place/v2/search?query=%s&output=%s&ak=%s&sn=%s&scope=%s&region=%s";
		// 调用sn计算函数,默认get请求
		$sn = $this->caculateAKSN ( $ak, $sk, $uri, $querystring_arrays );
		// 请求参数中有中文、特殊字符等需要进行urlencode,确保请求串与sn对应
		$target = sprintf ( $url, urlencode ( $query ), $output, $ak, $sn, $scope, urlencode ( $region ) );
		// 输出计算得到的sn
		// echo "sn: $sn \n";
		// 输出完整请求的url(仅供参考验证,故不能正常访问服务)
		// echo "url: $target \n";
		$content = file_get_contents ( $target );
		$content = json_decode ( $content, true );
		if ($content ['status'] == 0 && isset ( $content ['results'] ) && ! empty ( $content ['results'] ) && isset ( $content ['results'] [0] ['location'] ) && ! empty ( $content ['results'] [0] ['location'] )) {
			// var_export ( $content );
			return $content ['results'] [0] ['location'];
		}
		return false;
	}
	public function getAddressByLocation($longitude, $latitude) {
		$url = "http://api.map.baidu.com/geocoder/v2/?ak=thT0201pFtgt3VApRT2mhlA6mmpQUnWQ&location={$latitude},{$longitude}&output=json";
		$ch = curl_init ( $url );
		curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
		// 获取数据返回
		curl_setopt ( $ch, CURLOPT_BINARYTRANSFER, true );
		// 在启用
		// CURLOPT_RETURNTRANSFER
		// 时候将获取数据返回
		$output = curl_exec ( $ch );
		$arr = json_decode ( $output, true );
		if (! $arr || $arr ['status'] || ! isset ( $arr ['result'] ))
			return false;
		return $arr ['result'] ['formatted_address'];
	}
}

3

workerman+yii实现快速开发交互

示例:

http://24.igd.cc/workerman/page?caihaibin=ok

外部使用demo

http://24.igd.cc/workerman_remote/page?caihaibin=ok

http://gxu.igd.cn/workerman_remote/page?caihaibin=ok

(端口只有一个, 多个项目使用同一个workerman,通过特殊机制,实现将workerman请求转发到yii,并共享两边的sessiono数据,支持wss)

需要明确一个关键点,workerman本身是一个独立的运行容器,类似 apache+php的概念,独立运行.

http://24.igd.cc/workerman_remote/page?caihaibin=ok

因为 workerman 是一个独立的脚本容器,所以这类代码

lib ( ‘workerman/Gateway.php’ );
\GatewayClient\Gateway::joinGroup ( State ( ‘client_id’ ), WebSocketTools::KEY_PRE . ‘test’ );

其实比较像调用workerman的接口,实现相关功能.需要理解这块,所以两者有一定的时间差,这块初试化时,必须认login推送返回的消息为准,做为完全建立两者通讯的标志.如果没有,页面应当进行刷新,再次进行连接.有时websocket open是成功的,但是login消息也会没有推送回来,表示之前的网络通讯出了问题.当做不成功.

 

workerman 游戏的两种模式

第一种:

一种频率不高的,几秒甚至十几秒点一次的,如猜拳,24点小游戏.直接使用post调用yii动作实现游戏逻辑.

第二种:

频率非常高的,类似数钱,一秒会有数次请求的,全部走workerman session,不经过yii session 和数据库. 前台屏幕数据显示,可通过gatewayclient获取workerman所有的会话数据,再根据数据进行排名显示. 当比赛结束时,再将数据插入数据库.

//===============发送workerman 内部参数,用于直接调用workerman动作===============
// 自定义一个key 和 value ,一秒内多次推送也不影响性能
ws.send(JSON.stringify({model:’__workerman’,action:’set_value’,value_key:’score’,value_value:Math.random(0)}));
//===============发送workerman 内部参数,用于直接调用workerman动作

//gateway 获取session数据,不同项目,分组前缀必须不同,防止冲突

Gateway::getAllClientSessions ( WebSocketTools::KEY_PRE . ‘test’ )

广度workerman时序图

 

写给公司的yii1到yii2的转化

只针对模块形式代码进行yii1到yii2的转化,config中需要手动进行补充配置.
<?PHP
header ( 'Content-Type: text/html; charset=utf-8' );
include 'FileTools.php';
include 'lib.php';
// 需要转换的模块
$path = 'D:/PHPnow-1.5.6/htdocs/igddata/yii2sys/app/modules/member/';
// $path = 'D:/PHPnow-1.5.6/htdocs/igddata/yii2sys/app/';
// 根路径,用于识别命名空间
$root_path = 'D:/PHPnow-1.5.6/htdocs/igddata/yii2sys/';
$root_path = preg_replace ( '/\//', '\\', $root_path );
$preg_file = array ();
// Yii::$app->user->setState('_id',$identity->_id); 修改 成 Yii::$app->session->set ( '_id', $identity->getId () );
// ar getList findAll 需要针对性处理.
// CHTML
// <?
$preg_contents = array (
		'/Yii\:\:app\s*?\(\)/' => 'Yii::$app',
		// $app->params->membermodules => 变成数组 ['membermodules']
		'/Yii\:\:\$app\-\>params\-\>(\w+)/' => 'Yii::$app->params[\'\\1\']',
		// 直接调用\Yii跳过use
		'/(?<!\\\\)Yii/' => '\Yii',
		// CHTML 全部调整成 \yii\helpers\Html
		'/(?<!\\\\)CHTML/ims' => '\yii\helpers\Html',
		// 所有短标签自动补充成<?php
		'/\<\?(\s)/' => '<?php\\1',
		// post请求判断切换成yii2形式判断
		'/isPostRequest/' => 'isPost',
		'/getIsPostRequest\s*\(\s*\)/' => 'isPost',
		// user->setState 在新版本中已经被取消
		'/user\-\>setState/' => 'session\-\>set',
		// 统一当前登录用户获取用户id
		'/\>\_id/' => '>getId ()',
		// 所有原参数获取,调整为默认获取
		'/getParam\s*\(\s*?([\'\"\w]+)[^\)]*?\)/ims' => '$_REQUEST[\\1]',
		// '/render\s*\(/' => 'renderPartial(',
		// 渲染页面动作需要进行返回
		'/\n(\s*)\$this\-\>render/' => '
\\1return $this->render',
		// 统一当前登录用户获取用户id
		'/user\-\>\_id/' => 'user->getId()',
		// getAttributes()不能带false参数, 不然会导致出错
		'/getAttributes\s*\(\s*false\s*\)/' => 'getAttributes()',
		// ;;去除无效代码,但是for(;;)需要绕过
		'/(?<!\()\;\s*\;/' => ';',
		// 补充表前缀
		'/\{\{(?=\w)/' => '{{%',
		'/findByPk/i' => 'findOne',
		// 所有数组修改成新形式
		'/(?<!\w)array\s*\(([^\(\)]*?)\)/ms' => '[\\1]',
		'/jquery\-easyui\-1\.2\.5/ims' => 'jquery-easyui-1.3.1',
		'/public\s*function\s*tableName/ims' => 'public static function tableName',
		'/getAction\s*\(\)/' => 'action',
		// load为默认函数,不可用该函数名,全部调整为loadModel
		'/([\s\>])load\s*\(/' => '\\1loadModel(',
		// 修改http异常,并直接引用
		'/CHttpException/' => '\yii\web\HttpException',
		// 修改ar继承类
		'/extends CActiveRecord/' => 'extends \yii\db\ActiveRecord',
		// 修改注释,用于引入框架时,实现代码提示
		'/CActiveRecord/' => 'ActiveRecord',
		'/action\-\>getId\s*\(\)/' => 'action->id',
		'/getController\s*\(\)/' => 'controller',
		'/controller\-\>getId\s*\(\)/' => 'controller->id',
		'/getModule\s*\(\)/' => 'module',
		'/module\-\>getId\s*\(\)/' => 'module->id',
		// array 引用
		'/require\_once\s*\(\s*[\'\"]array\.php[\'\"]\s*\)\;/ims' => 'lib(\'array.php\');',
		'/require\_once\s*\(\s*[\'\"]array2\.php[\'\"]\s*\)\;/ims' => 'lib(\'array2.php\');',
		// 去除WebControls引用
		'/require\_once\s*\(\s*[\'\"]WebControls\.php[\'\"]\s*\)\;/' => '',
		// 直接调用WebControls
		'/new WebControls/' => 'new \ext\WebControls',
		// 去除MysqlTools引用
		'/require\_once\s*\([\'\"]MysqlTools\.php[\'\"]\)\;/' => '',
		'/lib\s*\(\s*[\'\"]MysqlTools\.php[\'\"]\s*\)\;/' => '',
		// 直接调用MysqlTools
		'/(?<!\\\\)MysqlTools/' => '\ext\MysqlTools',
		// GCheckData直接转化
		'/(?<!\\\\)GCheckData/' => '\ext\GCheckData',
		// 替换控制器集成类
		'/extends\s*CController/' => 'extends \yii\web\Controller',
		// beforeAction只能为公开函数
		'/protected\s*function\s*beforeAction/ims' => 'public function beforeAction',
		// 判断到有beforeAction的,需要进行父函数调用,否则会导致过滤器失效
		'/beforeAction/' => function ($path, $content) {
			// 有使用before,但是没有进行父函数调用,需要补充父类beforeAction调用
			if (! preg_match ( '/parent\:\:beforeAction/', $content )) {
				$content = preg_replace ( '/beforeAction[^\{]+?\{/ims', 'beforeAction($action) {
		// 需要调用父函数,否则不会触发过滤器
		if (!parent::beforeAction ( $action )) {
			return false;
		}', $content );
			}
			return $content;
		},
		// 整个替换module.php内容,不然配置文件需要手动修改.
		'/CWebModule/' => function ($path, $content) {
			$namespace = getNamespaceByPath ( $path );
			$content = <<<EOF
<?php
namespace {$namespace};
class Module extends \yii\base\Module {
	public function init() {
		parent::init ();
	}
}
EOF;
			return $content;
		},
		// ar特殊处理,补充命名空间
		// 需要先进行命名空间的添加,用于控制器,页面添加对象时,直接识别命名空间,并添加
		'/\\\\yii\\\\db\\\\ActiveRecord/' => function ($path, $content) {
			$namespace = getNamespaceByPath ( $path );
			// \s和(?!)正向零宽断言会导致误判,需要后面补充一个标志
			if (! preg_match ( '/^\<\?php\s*namespace/is', $content )) {
				$content = preg_replace ( '/^\<\?php\s*/ims', "<?php\nnamespace {$namespace};\n", $content );
			}
			// ArticleX 的model有特殊实现,不进行替换.
			if (! preg_match ( '/ArticleX/', $path ) && preg_match ( '/function\s*model\s*\(/ims', $content )) {
				$content = preg_replace ( '/function\s*model\s*\([^\}]+?\}/ims', 'function model($className = __CLASS__) {
		return new self ();
	}', $content );
			}
			if (preg_match ( '/function\s*getList\s*\(\s*\$criteria\s*\=\s*null/ims', $content )) {
				$content = preg_replace ( '/function\s*getList\s*\(\s*\$criteria\s*\=\s*null\s*/ims', 'function getList($afmodel = null', $content );
				$content = preg_replace ( '/(function\s*getList\s*\(\s*\$afmodel\s*\=\s*null\s*[^\;]+?)\$this\-\>findAll\s*\(\s*\$criteria\s*\)\s*\;/ims', '\\1$afmodel->all();', $content );
			}
			if (preg_match ( '/function\s*getCount\s*\(\s*\$criteria\s*\=\s*null/ims', $content )) {
				$content = preg_replace ( '/function\s*getCount\s*\(\s*\$criteria\s*\=\s*null\s*/ims', 'function getCount($afmodel = null', $content );
				$content = preg_replace ( '/(function\s*getCount\s*\(\s*\$afmodel\s*\=\s*null\s*[^\;]+?)\$this\-\>count\s*\(\s*\$criteria\s*\)\s*\;/ims', '\\1$afmodel->count();', $content );
			}
			return $content;
		},
		// 控制器特殊处理,补充命名空间,修改过滤器
		'/\\\\yii\\\\web\\\\Controller/' => function ($path, $content) {
			$namespace = getNamespaceByPath ( $path );
			$models_namespace = getNamespaceByPath ( dirname ( $path ) ) . '\\models';
			
			// 修改过滤器
			// 其它特殊模块,照样修改
			if (preg_match ( '/public\s*function\s*filters\(\s*\)\s*\{[^}]+?\}/ims', $content )) {
				$string = <<<EOF
public function behaviors() {
		return [ 
				'access' => [ 
						'class' => \\ext\\RbacFilter::className () 
				] 
		];
	}				
EOF;
				$content = preg_replace ( '/public\s*function\s*filters\(\s*\)\s*\{[^}]+?\}/ims', $string, $content );
			}
			// 先获取ar列表,并添加到use中,再添加命名空间
			// 获取所有ar,并标记到use中和页面中
			$models = getModelsByControllerPath ( $path );
			$use_models = array ();
			foreach ( $models as $model ) {
				$model_ns = $models_namespace . '\\' . $model;
				// 判断是否有该类同时判断是否已添加命名空间
				if (preg_match ( "/\W{$model}\W/ms", $content ) && ! stristr ( $content, $model_ns )) {
					$content = preg_replace ( '/^\<\?php\s*/ims', "<?php\nuse \\{$model_ns};\n", $content );
					$use_models [] = $model;
				}
			}
			// 控制器补充命名空间使用
			// \s和(?!)正向零宽断言会导致误判,需要后面补充一个标志
			// 命名空间前面不能直接加反斜杠,use 前可以
			if (! preg_match ( '/^\<\?php\s*namespace/ims', $content )) {
				$content = preg_replace ( '/^\<\?php\s*/ims', "<?php\nnamespace {$namespace};\n", $content );
			}
			// 页面补充命名空间使用
			$views = getViewsByControllerPath ( $path );
			foreach ( $views as $view ) {
				$page_content = file_get_contents ( $view );
				// 去除头部和尾部空白部分
				$page_content = trim ( $page_content );
				// 如果没有php头,就补充php头
				if (preg_match ( '/^(?!\<\?php\s*)/i', $page_content )) {
					$page_content = preg_replace ( '/^(?!\<\?php\s*)/i', '<?php

?>', $page_content );
				}
				$use_models = array ();
				foreach ( $models as $model ) {
					$model_ns = $models_namespace . '\\' . $model;
					// 判断是否有该类同时判断是否已添加命名空间
					if (preg_match ( "/\W{$model}\W/ms", $page_content ) && ! stristr ( $page_content, $model_ns )) {
						$page_content = preg_replace ( '/^\<\?php\s*/is', "<?php\nuse \\{$model_ns};\n", $page_content );
					}
				}
				file_put_contents ( $view, $page_content );
			}
			return $content;
		},
		// $criteria =new CDbCriteria 及分页逻辑实现自动转化
		'/\$criteria\s*\=\s*new\s*CDbCriteria.+?(\$this|\$model|\w+|\w+\:\:model\s*\(\s*\))\-\>(findAll|find|getList)\s*\([^\;]*?\)\;/ims' => function ($path, $content, $preg) {
			while ( preg_match ( $preg, $content, $matchs ) ) {
				$subcontent = $matchs [0];
				$object = $matchs [1];
				// 如果是xxx::model()和$this ,需要对(:,(,),$)补充斜杠,进行转义处理,用于正则表达式
				if (preg_match ( '/([\:\(\)\$])/', $object )) {
					$preg_object = preg_replace ( '/([\:\(\)\$])/', '\\\\\\1', $object );
				}
				// 调试识别结果
				// gddebug ( $preg_object, $object, $subcontent, $matchs );
				// exit ();
				// 原CDbCriteria编程ActiveQuery形式,直接通过find返回该对象,取代.
				$subcontent = preg_replace ( '/\$criteria\s*\=\s*new\s*CDbCriteria\s*(\(\s*\))?\;/ims', "\$afmodel=(\$afmodel?\$afmodel:{$object}->find());", $subcontent );
				// CDbCriteria condition 转化,全部通过andWhere代替,重叠部分,会自动补充and处理
				$subcontent = preg_replace ( '/\$criteria\s*\-\>condition\s*\.?\s*\=\s*([^\;]+?)\;/ims', "\$afmodel->andWhere(\\1);", $subcontent );
				// CDbCriteria addCondition 转化,全部通过andWhere代替,重叠部分,会自动补充and处理
				$subcontent = preg_replace ( '/\$criteria\s*\-\>addCondition\s*\(([^\;]+?)\)\;/ims', "\$afmodel->andWhere(\\1);", $subcontent );
				// CDbCriteria order 转化
				$subcontent = preg_replace ( '/\$criteria\s*\-\>order\s*\.?\s*\=\s*([^\;]+?)\;/ims', "\$afmodel->orderBy(\\1);", $subcontent );
				// CDbCriteria select 转化
				$subcontent = preg_replace ( '/\$criteria\s*\-\>select\s*\.?\s*\=\s*([^\;]+?)\;/ims', "\$afmodel->select(\\1);", $subcontent );
				// ActiveRecord count 转化
				$subcontent = preg_replace ( '/' . $preg_object . '?\s*\-\>count\s*\([^\;]*?\)\;/ims', "\$afmodel->count();", $subcontent );
				
				// 如果出现特殊自定函数,修改传入参数
				$subcontent = preg_replace ( '/\-\>getCount\s*\(\s*\$criteria\s*\)/', '->getCount($afmodel)', $subcontent );
				
				// CPagination分页对象转化
				$subcontent = preg_replace ( '/new\s*CPagination\s*\(([^\)]+?)\)/ims', 'new \yii\data\Pagination ( [ 
				"totalCount" => \\1
		] )', $subcontent );
				// 分页大小转化
				$subcontent = preg_replace ( '/\$pages\s*\-\>\s*pageSize\s*\=([^\;]+)\;/ims', "\$pages->setPageSize ( \\1 );", $subcontent );
				// $pages->applyLimit($criteria) 没用,替换成page变量设置及分页变量带入ActiveQuery处理
				$subcontent = preg_replace ( '/\$pages\s*\-\>\s*applyLimit([^\;]+)\;/ims', "\$pages->setPage ( \$page-1 );
			\$afmodel->offset ( \$pages->offset )->limit ( \$pages->limit );", $subcontent );
				// ActiveRecord::findAll转化成 ActiveQuery::all()
				$subcontent = preg_replace ( '/' . $preg_object . '\s*\-\>(findAll|find\s*\(\s*\)\-\>)\s*\([^\;]*?\)\;/ims', "\$afmodel->all();", $subcontent );
				// $model->getList进行特殊处理
				$subcontent = preg_replace ( '/' . $preg_object . '\s*\-\>getList\s*\([^\;]*?\)\;/ims', "{$object}->getList(\$afmodel);", $subcontent );
				// 所有andWhere 内多余的and进行剔除
				$subcontent = preg_replace ( '/andWhere\s*\(\s*(\'|\")\s*and/ims', 'andWhere(\\1', $subcontent );
				// 调试转化结果
				// gddebug ( $subcontent );
				// exit ();
				$content = str_replace ( $matchs [0], $subcontent, $content );
			}
			return $content;
		},
		'/(url\(\'\w+\'\)\?\>)\/(\w+)\//ims' => '\\1&\\2=',
		// 原find(where)调整成find()->where(where)->one()
		'/\-\>find\s*\(([^\)]+)\)/ims' => '->find ()->where (\\1)->one ()',
		// 原findAll(where)调整成find()->where(where)->all()
		'/\-\>findAll\s*\(([^\)]+)\)/ims' => '->find ()->where (\\1)->all ()',
		// count 替换成 find()->where()->count();
		'/\-\>count\s*\(([^\)]+)\)/ims' => '->find ()->where (\\1)->count ()',
		// deleteByPk转化成findOne()->delete();
		'/\-\>deleteByPk\s*\(([^\)]+)\)/ims' => '->findOne(\\1)->delete ()',
		// 页面url自动转化成全小写
		'/(?:\W)url\s*\(\s*\'[^\']+\'/ims' => function ($path, $content) {
			$content = preg_replace_callback ( '/url\s*\(\s*\'[^\']+\'/ims', '_strtolower', $content );
			return $content;
		},
		// 控制器,动作,只首字母大写,后半部分全部小写
		'/function\s*action(\w)\s*\(\s*\)/ims' => function ($path, $content) {
			$content = preg_replace_callback ( '/function\s*action(\w+)\s*\(\s*\)/ims', '_ucfirst', $content );
			return $content;
		} 
);
function _strtolower($matches) {
	return strtolower ( $matches [0] );
}
function _ucfirst($matches) {
	$action = strtolower ( $matches [1] );
	// actions 为过滤器配置,不进行过滤处理
	if ($action === 's') {
		return 'function actions()';
	}
	return 'function action' . ucfirst ( $action ) . '()';
}
function getModelsByControllerPath($path) {
	$models_dir = dirname ( dirname ( $path ) ) . '/models';
	$list = FileTools::getCurList ( $models_dir );
	if (! $list)
		return array ();
	$items = array ();
	foreach ( $list as $item ) {
		$filename = basename ( $item );
		$classname = str_replace ( '.php', '', $filename );
		$items [$classname] = $classname;
	}
	return $items;
}
function getViewsByControllerPath($path) {
	$ctlName = basename ( str_replace ( 'Controller.php', '', $path ) );
	$ctlName = strtolower ( $ctlName );
	$models_dir = dirname ( dirname ( $path ) ) . '/views/' . $ctlName;
	$list = FileTools::getCurList ( $models_dir );
	if (! $list)
		return array ();
	$items = array ();
	foreach ( $list as $item ) {
		$filename = basename ( $item );
		$classname = str_replace ( '.php', '', $filename );
		$items [$classname] = $item;
	}
	return $items;
}
function getNamespaceByPath($path) {
	global $root_path;
	$path = dirname ( $path );
	$path = preg_replace ( '/\//', '\\', $path );
	return str_replace ( $root_path, '', $path );
}
// sql 中,pow需要手动检查,替换成 power ,pi 需要手动检查,替换成 acos(-1)

$preg_file = array (
		'/^\w+Module\.php$/' => 'Module.php' 
);
FileTools::replacenames ( $path, $preg_file );
FileTools::replaceContents ( $path, $preg_contents, array (
		'/\.php$/i' => false,
		'/yiic/i' => true 
) );

//直接根据列表文件进行替换规则处理


 

模块自动转化工具

/yii2sys/_cover_tools/replacefile_to_yii2.php
通过配置指定目录,脚本可实现yii1绝大部分自动转换到yii2形式
主要区别
yii2补充命名空间
工具已实现页面和控制器,自动引入models相关类,model类和控制器自动追加命名空间
全局类引入,非常用类型,直接以\yii\xxx 或\ext\xxx形式带入,减少不必要引入代码
rbac
基本和yii1相同,但是一些数据字段名做了调整(如:adminid调整为admin_id,很多类似字段都补充了下划线)
ar的查询方式
通过find的形式返回ActiveQuery对象,通过对该对象进行操作实现查询
原 CDbCriteria 对象被取消,工具已实现常用查询类的自动转化,关联查询仍然需要进行手动补充调整.
关联查询,通过配置查询对象,实现分页,主要特殊在主表和子表重命名,具体看项目案例.
分页对象做了调整,默认$_GET[‘page’]不做为默认分页值,需要手动设置
系统结构调整
/system 原模块被整合到/app/modules/里
框架结构
/yii2framework
使用yii2高级模板,已做结构的调整同时项目yii2sys也做了调整,使现有结构形式按原来的部署方式
(注:有需要可自行下载原高级模板进行了解)
编辑器使用
为实现快速开发,所有有ar返回的函数,函数注释可以通过补充@return ActiveRecord 实现代码提示(需要在inlucde_path中引入框架做为附属项目)
主要修改点整理:
url() yii2fix module 默认会到根当成模块
导致顶根判断异常,所以需要跳过
自定义state
yii2已移除state,官方说法是避免混乱.导致无法同域名多应用session共享冲突
md5版本
可以实现端口共享session
域名+目录+程序id版本
实现多项目时,自动区分session数据
\Wechat 类第三方重写,可能类很多,编写时,统一全部认根域名.实现快速重写
HeadersAlreadySentException 异常
// ini_set ( ‘output_buffering’, 0 );设置无效,还是需要通过修改配置.不然超出会抛
__CLASS__ 会带入命名空间路径
php 7.2 下 upload 上传兼容 media data missing hint 异常
兼容方式为 curl_file_create 形式
Yii2 已 去除任意参数 路径化处理.
需要路径化的参数,只能通过配置实现. 前台除了控制器经过重写外,其它参数全部都以 ? 形式拼接
\yii\helpers\Html::DropDownList 不再自动代入id,需要通过htmloptions配置进去
CHtml 调整成 Html
CWenhaoPager.php 重写成 LinkPager 形式
反射类调用,需要写全命名空间
计划任务
表前缀 {{%table_name}} 多了个 “%”
入口环境 测试环境(保留) 开发环境 ,生产环境 切换
将/system 移动到 app/modules/system 做为 app 下的模块
Yii::$app->createUrl 换成 Yii::$app->urlManager->createUrl
CDbCriteria
$models = (new \app\modules\system\models\Menu ())->find ()->where ( “parentId=0 and state=1” )->orderBy ( ‘ordernum asc,id asc’ )->all ();
ar 修改成yii2形式,需要实现接口继承+覆盖几个内置函数
$model->getAttributes ( false ); false 去掉 ,默认动作只识别null
Yii::$app->request->isPostRequest 替换成 Yii::$app->request->post ()
Yii::$app->controller->getAction ()->getId () Yii::$app->controller->action->id
Yii::$app->controller->getId () Yii::$app->controller->id
Yii::$app->controller->getModule () Yii::$app->controller->module->id
controllers 控制器 render 类动作,需要返回. 变成 return $this->render…
enableCsrfValidation 全局.暂时csrf校验关闭,默认开启.动作在action之前
后台基本版 igd_system_adminauthassignment 表 userid 调整为 user_id (框架写死字段)
beforeAction 不能直接返回true,需要调用父函数,否则会导致过滤器无法触发
数据库权限判断 app ()->authManager->checkAccess ( $user_id, $permission ) 参数顺序变化
表重命名
AdminLog::model ()->find ()->alias ( ‘t’ );
表链接
AdminLog::model ()->find ()->innerJoinWith ( [
// 关联表,重命名
‘admin as a’
] )
http 异常抛出调整
throw new CHttpException ( 404, ‘The ‘ . __CLASS__ . ‘ does not exist.’ );
throw new \yii\web\HttpException ( 404, ‘The ‘ . __CLASS__ . ‘ does not exist.’ );
动态表限制实在太多.后续有需要,需要通过ArticleX->fixTableName进行修正
原find(where)调整成find()->where(where)->one()
原findAll(where)调整成find()->where(where)->all()

preg_replace return NULL 解决

本来以为自己对正则已经很熟悉,最近又学到了一个,虽然是配置上的问题,不过也学习了。
使用preg_replace 处理长字符串时,返回null的原因

http://de.php.net/manual/de/pcre.configuration.php

官方文档

这些函数的行为受 php.ini 中的设置影响。

PCRE配置选项
名字	默认	可修改范围	更新日志
pcre.backtrack_limit	"100000"	PHP_INI_ALL	php 5.2.0可用.
pcre.

 

"100000" PHP_INI_ALL php 5.2.0可用. 有关 PHP_INI_* 样式的更多详情与定义,见 配置可被设定范围。 这是配置指令的简短说明。 pcre.backtrack_limit integer PCRE的回溯限制. pcre.recursion_limit integer PCRE的递归限制. 请注意, 如果 讲这个值设置为一个很大的数字, 你可能会消耗掉 所有的进程可用堆栈, 最终导致php崩溃(直到达到系统限制的堆栈大小).

由于在匹配到前,内容太多,回溯和匹配过多,导致NULL

所以只要

ini_set('pcre.backtrack_limit',1000000);
ini_set('pcre.recursion_limit',1000000);
//通常够用,不沟通,适当加大即可。

phpjquery & simpledomphp 比较

phpjquery

https://github.com/TobiaszCudnik/phpquery

simplehtmldom

http://simplehtmldom.sourceforge.net/

<?php

function gddebug() {
	$arrays = func_get_args ();
	echo "n<pre>";
	foreach ( $arrays as $value ) {
		echo "==============================================rn";
		if (is_array ( $value ) || is_object ( $value )) {
			ksort ( $value );
			echo htmlspecialchars ( print_r ( $value, true ) ) . "rn";
		} elseif (is_string ( $value )) {
			echo "string(" . strlen ( $value ) . ") "" . htmlspecialchars ( $value ) . ""rn";
		} else {
			var_dump ( $value );
		}
	}
	echo "</pre>";
}
gddebug ( 'init', memory_get_usage () );
require ('phpQuery/phpQuery.php');
gddebug ( 'load libs', memory_get_usage () );

// INITIALIZE IT
// phpQuery::newDocumentHTML($markup);
// phpQuery::newDocumentXML();
// phpQuery::newDocumentFileXHTML('test.html');
// phpQuery::newDocumentFilePHP('test.php');
// phpQuery::newDocument('test.xml', 'application/rss+xml');
// this one defaults to text/html in utf8
// 文件 内容
// //https://www.google.com.sg/search?hl=zh-CN&site=imghp&tbm=isch&source=hp&biw=1366&bih=653&q=271072+CC240+armani&oq=271072+CC240+armani&gs_l=img.12...5739.5739.0.6570.1.1.0.0.0.0.120.120.0j1.1.0....0...1ac..64.img..1.0.0.Md5Dux9zIdI
$doc = phpQuery::newDocumentFileHTML ( 'test.html' );

gddebug ( 'load file', memory_get_usage () );
$obj = $doc->find ( '.rg_di' );
gddebug ( 'load elements', memory_get_usage (), 'count', count ( $obj ) );

输出

==============================================
string(4) "init"
==============================================
int(62208)
==============================================
string(9) "load libs"
==============================================
int(1441040)
==============================================
string(9) "load file"
==============================================
int(1445448)
==============================================
string(13) "load elements"
==============================================
int(1469744)
==============================================
string(5) "count"
==============================================
int(100)

===========================simple html dom

<?php

function gddebug() {
	$arrays = func_get_args ();
	echo "n<pre>";
	foreach ( $arrays as $value ) {
		echo "==============================================rn";
		if (is_array ( $value ) || is_object ( $value )) {
			ksort ( $value );
			echo htmlspecialchars ( print_r ( $value, true ) ) . "rn";
		} elseif (is_string ( $value )) {
			echo "string(" . strlen ( $value ) . ") "" . htmlspecialchars ( $value ) . ""rn";
		} else {
			var_dump ( $value );
		}
	}
	echo "</pre>";
}
gddebug ( 'init', memory_get_usage () );
include 'simple_html_dom.php';
gddebug ( 'load libs', memory_get_usage () );

// INITIALIZE IT
// phpQuery::newDocumentHTML($markup);
// phpQuery::newDocumentXML();
// phpQuery::newDocumentFileXHTML('test.html');
// phpQuery::newDocumentFilePHP('test.php');
// phpQuery::newDocument('test.xml', 'application/rss+xml');
// this one defaults to text/html in utf8

// //https://www.google.com.sg/search?hl=zh-CN&site=imghp&tbm=isch&source=hp&biw=1366&bih=653&q=271072+CC240+armani&oq=271072+CC240+armani&gs_l=img.12...5739.5739.0.6570.1.1.0.0.0.0.120.120.0j1.1.0....0...1ac..64.img..1.0.0.Md5Dux9
$doc = file_get_html ( 'test.html' );

gddebug ( 'load file', memory_get_usage () );
$obj = $doc->find ( '.rg_di' );
gddebug ( 'load elements', memory_get_usage (), 'count', count ( $obj ) );

 

==============================================
string(4) "init"
==============================================
int(62072)
==============================================
string(9) "load libs"
==============================================
int(527136)
==============================================
string(9) "load file"
==============================================
int(5590512)
==============================================
string(13) "load elements"
==============================================
int(5596016)
==============================================
string(5) "count"
==============================================
int(100)

不用多言,内存使用上完败。。

还只是一个小页面,大页面,基本上simplehtmldom经常性抛出内存不足,需要很很好的进行clear()和unset 来释放内存。同时在解析目标元素时,需要通过正则将目标元素前后元素删除才不会占用过多内存。使用上,phpjquery更接近jquery,个人比较喜欢这类。

本来最近频繁帮朋友采集各种在线商店的商品数据,嫌simple太繁琐,准备自己写个脚本,分离出需要保留的元素来的(包含子节点)。后续研究下phpjquery有没有类似的,没有就自己写一个出来,先占位~

坐标系纠偏,转换类

缘由:常见各种坐标系由来和对应关系

WGS84、Web墨卡托、火星坐标、百度坐标互转

http://blog.csdn.net/wildboy2001/article/details/12031351

自己取的点为什么在地图上显示不准?

http://lbs.amap.com/home/faq/%E5%9D%90%E6%A0%87%E4%BD%93%E7%B3%BB/#Q03

常见坐标系对应关系

1.GPS/原始坐标系 WGS-84
2.百度 /加密坐标系 BD-09
3.谷歌/腾讯 GCJ-02

转换类

声明:类为复制

<?php
/**
 * 1.GPS/原始坐标系   WGS-84
 * 2.百度 /加密坐标系   BD-09
 * 3.谷歌/腾讯     GCJ-02
 */
class GPS {
	private $PI = 3.14159265358979324;
	private $x_pi = 0;
	public function __construct() {
		$this->x_pi = 3.14159265358979324 * 3000.0 / 180.0;
	}
	// WGS-84 to GCJ-02
	public function gcj_encrypt($wgsLat, $wgsLon) {
		if ($this->outOfChina ( $wgsLat, $wgsLon ))
			return array (
					'lat' => $wgsLat,
					'lon' => $wgsLon 
			);

		$d = $this->delta ( $wgsLat, $wgsLon );
		return array (
				'lat' => $wgsLat + $d ['lat'],
				'lon' => $wgsLon + $d ['lon'] 
		);
	}
	// GCJ-02 to WGS-84
	public function gcj_decrypt($gcjLat, $gcjLon) {
		if ($this->outOfChina ( $gcjLat, $gcjLon ))
			return array (
					'lat' => $gcjLat,
					'lon' => $gcjLon 
			);

		$d = $this->delta ( $gcjLat, $gcjLon );
		return array (
				'lat' => $gcjLat - $d ['lat'],
				'lon' => $gcjLon - $d ['lon'] 
		);
	}
	// GCJ-02 to WGS-84 exactly
	public function gcj_decrypt_exact($gcjLat, $gcjLon) {
		$initDelta = 0.01;
		$threshold = 0.000000001;
		$dLat = $initDelta;
		$dLon = $initDelta;
		$mLat = $gcjLat - $dLat;
		$mLon = $gcjLon - $dLon;
		$pLat = $gcjLat + $dLat;
		$pLon = $gcjLon + $dLon;
		$wgsLat = 0;
		$wgsLon = 0;
		$i = 0;
		while ( TRUE ) {
			$wgsLat = ($mLat + $pLat) / 2;
			$wgsLon = ($mLon + $pLon) / 2;
			$tmp = $this->gcj_encrypt ( $wgsLat, $wgsLon );
			$dLat = $tmp ['lat'] - $gcjLat;
			$dLon = $tmp ['lon'] - $gcjLon;
			if ((abs ( $dLat ) < $threshold) && (abs ( $dLon ) < $threshold))
				break;

			if ($dLat > 0)
				$pLat = $wgsLat;
			else
				$mLat = $wgsLat;
			if ($dLon > 0)
				$pLon = $wgsLon;
			else
				$mLon = $wgsLon;

			if (++ $i > 10000)
				break;
		}
		// console.log(i);
		return array (
				'lat' => $wgsLat,
				'lon' => $wgsLon 
		);
	}
	// GCJ-02 to BD-09
	public function bd_encrypt($gcjLat, $gcjLon) {
		$x = $gcjLon;
		$y = $gcjLat;
		$z = sqrt ( $x * $x + $y * $y ) + 0.00002 * sin ( $y * $this->x_pi );
		$theta = atan2 ( $y, $x ) + 0.000003 * cos ( $x * $this->x_pi );
		$bdLon = $z * cos ( $theta ) + 0.0065;
		$bdLat = $z * sin ( $theta ) + 0.006;
		return array (
				'lat' => $bdLat,
				'lon' => $bdLon 
		);
	}
	// BD-09 to GCJ-02
	public function bd_decrypt($bdLat, $bdLon) {
		$x = $bdLon - 0.0065;
		$y = $bdLat - 0.006;
		$z = sqrt ( $x * $x + $y * $y ) - 0.00002 * sin ( $y * M_PI );
		$theta = atan2 ( $y, $x ) - 0.000003 * cos ( $x * M_PI );
		$gg_lon = $z * cos ( $theta );
		$gg_lat = $z * sin ( $theta );
		// 方式2
		return array (
				'lat' => $gg_lat,
				'lon' => $gg_lon 
		);
		//
		$x = $bdLon - 0.0065;
		$y = $bdLat - 0.006;
		$z = sqrt ( $x * $x + $y * $y ) - 0.00002 * sin ( $y * $this->x_pi );
		$theta = atan2 ( $y, $x ) - 0.000003 * cos ( $x * $this->x_pi );
		$$gcjLon = $z * cos ( $theta );
		$gcjLat = $z * sin ( $theta );
		return array (
				'lat' => $gcjLat,
				'lon' => $gcjLon 
		);
	}
	public function bd_decrypt_lat($bdLat, $bdLon) {
		$result = $this->bd_decrypt ( $bdLat, $bdLon );
		return $result ['lat'];
	}
	public function bd_decrypt_lon($bdLat, $bdLon) {
		$result = $this->bd_decrypt ( $bdLat, $bdLon );
		return $result ['lon'];
	}

	// WGS-84 to Web mercator
	// $mercatorLat -> y $mercatorLon -> x
	public function mercator_encrypt($wgsLat, $wgsLon) {
		$x = $wgsLon * 20037508.34 / 180.;
		$y = log ( tan ( (90. + $wgsLat) * $this->PI / 360. ) ) / ($this->PI / 180.);
		$y = $y * 20037508.34 / 180.;
		return array (
				'lat' => $y,
				'lon' => $x 
		);
		/*
		 * if ((abs($wgsLon) > 180 || abs($wgsLat) > 90)) return NULL; $x =
		 * 6378137.0 * $wgsLon * 0.017453292519943295; $a = $wgsLat *
		 * 0.017453292519943295; $y = 3189068.5 * log((1.0 + sin($a)) / (1.0 -
		 * sin($a))); return array('lat' => $y, 'lon' => $x); //
		 */
	}
	// Web mercator to WGS-84
	// $mercatorLat -> y $mercatorLon -> x
	public function mercator_decrypt($mercatorLat, $mercatorLon) {
		$x = $mercatorLon / 20037508.34 * 180.;
		$y = $mercatorLat / 20037508.34 * 180.;
		$y = 180 / $this->PI * (2 * atan ( exp ( $y * $this->PI / 180. ) ) - $this->PI / 2);
		return array (
				'lat' => $y,
				'lon' => $x 
		);
		/*
		 * if (abs($mercatorLon) < 180 && abs($mercatorLat) < 90) return NULL;
		 * if ((abs($mercatorLon) > 20037508.3427892) || (abs($mercatorLat) >
		 * 20037508.3427892)) return NULL; $a = $mercatorLon / 6378137.0 *
		 * 57.295779513082323; $x = $a - (floor((($a + 180.0) / 360.0)) *
		 * 360.0); $y = (1.5707963267948966 - (2.0 * atan(exp((-1.0 *
		 * $mercatorLat) / 6378137.0)))) * 57.295779513082323; return
		 * array('lat' => $y, 'lon' => $x); //
		 */
	}
	// two point's distance
	public function distance($latA, $lonA, $latB, $lonB) {
		$earthR = 6371000.;
		$x = cos ( $latA * $this->PI / 180. ) * cos ( $latB * $this->PI / 180. ) * cos ( ($lonA - $lonB) * $this->PI / 180 );
		$y = sin ( $latA * $this->PI / 180. ) * sin ( $latB * $this->PI / 180. );
		$s = $x + $y;
		if ($s > 1)
			$s = 1;
		if ($s < - 1)
			$s = - 1;
		$alpha = acos ( $s );
		$distance = $alpha * $earthR;
		return $distance;
	}
	private function delta($lat, $lon) {
		// Krasovsky 1940
		//
		// a = 6378245.0, 1/f = 298.3
		// b = a * (1 - f)
		// ee = (a^2 - b^2) / a^2;
		$a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
		$ee = 0.00669342162296594323; // ee: 椭球的偏心率。
		$dLat = $this->transformLat ( $lon - 105.0, $lat - 35.0 );
		$dLon = $this->transformLon ( $lon - 105.0, $lat - 35.0 );
		$radLat = $lat / 180.0 * $this->PI;
		$magic = sin ( $radLat );
		$magic = 1 - $ee * $magic * $magic;
		$sqrtMagic = sqrt ( $magic );
		$dLat = ($dLat * 180.0) / (($a * (1 - $ee)) / ($magic * $sqrtMagic) * $this->PI);
		$dLon = ($dLon * 180.0) / ($a / $sqrtMagic * cos ( $radLat ) * $this->PI);
		return array (
				'lat' => $dLat,
				'lon' => $dLon 
		);
	}
	private function outOfChina($lat, $lon) {
		if ($lon < 72.004 || $lon > 137.8347)
			return TRUE;
		if ($lat < 0.8293 || $lat > 55.8271)
			return TRUE;
		return FALSE;
	}
	private function transformLat($x, $y) {
		$ret = - 100.0 + 2.0 * $x + 3.0 * $y + 0.2 * $y * $y + 0.1 * $x * $y + 0.2 * sqrt ( abs ( $x ) );
		$ret += (20.0 * sin ( 6.0 * $x * $this->PI ) + 20.0 * sin ( 2.0 * $x * $this->PI )) * 2.0 / 3.0;
		$ret += (20.0 * sin ( $y * $this->PI ) + 40.0 * sin ( $y / 3.0 * $this->PI )) * 2.0 / 3.0;
		$ret += (160.0 * sin ( $y / 12.0 * $this->PI ) + 320 * sin ( $y * $this->PI / 30.0 )) * 2.0 / 3.0;
		return $ret;
	}
	private function transformLon($x, $y) {
		$ret = 300.0 + $x + 2.0 * $y + 0.1 * $x * $x + 0.1 * $x * $y + 0.1 * sqrt ( abs ( $x ) );
		$ret += (20.0 * sin ( 6.0 * $x * $this->PI ) + 20.0 * sin ( 2.0 * $x * $this->PI )) * 2.0 / 3.0;
		$ret += (20.0 * sin ( $x * $this->PI ) + 40.0 * sin ( $x / 3.0 * $this->PI )) * 2.0 / 3.0;
		$ret += (150.0 * sin ( $x / 12.0 * $this->PI ) + 300.0 * sin ( $x / 30.0 * $this->PI )) * 2.0 / 3.0;
		return $ret;
	}
}

 

soap 超时处理

class SoapClientX extends SoapClient {
	private $timeout;
	function __construct($wdsl, $options = array()) {
		// 考虑到不和原函数冲突.
		if (isset ( $options ['timeout'] )) {
			$this->timeout = intval ( $options ['timeout'] );
		} else {
			$this->timeout = 10;
		}
		parent::__construct ( $wdsl, $options );
	}
	public function __doRequest($request, $location, $action, $version, $one_way = false) {
		ini_set ( 'default_socket_timeout', $this->timeout );
		try {
			// 当超时返回空时,表示非正常报文返回,出现"Error Fetching http headers"异常
			$response = parent::__doRequest ( $request, $location, $action, $version, $one_way );
		} catch ( Exception $e ) {
			if (stristr ( $e->getMessage (), "Error Fetching http headers" )) {
				throw new SoapFault ( 'timeout', "请求超时" );
			}
		}
		if (! $response) {
			throw new SoapFault ( 'timeout', "请求超时" );
		}
		return ($response);
	}
}

 

curl 快速伪造登录抓取数据

很多时候为了抓取某些特殊页面的页面数据,特别是补充了cookie校验或者处理逻辑的。一些登录后才可进行访问的页面数据。往往需要从登录到后面的操作一步一步实现,再对数据进行特殊处理.

下面提供一种相对临时,快捷的方式。

用chrome访问需要访问的页面,如果需要登录,登录即可。

将报文头部分全部复制,然后配到curl的头中。此时的session标志是已登录状态。或者已被服务器标志为正常用户了。

 

	
	$ch = curl_init ();
	curl_setopt ( $ch, CURLOPT_URL, $url );
	curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
	$header[]='Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8';
// 	$header[]='Accept-Encoding:gzip,deflate,sdch';
	$header[]='Accept-Language:zh-CN,zh;q=0.8';
	$header[]='Cache-Control:max-age=0';
	$header[]='Connection:keep-alive';
	$header[]='Cookie:_xxxxxxxxxx';
	$header[]='Host:xxxxxxxxxxxxxxxx';
	$header[]='Referer:http://xxxxxxxxxxxx';
	$header[]='User-Agent:Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36';

	curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
	$contents = curl_exec ( $ch );
	curl_close ( $ch );

这句是压缩,需要隐藏。

// $header[]=’Accept-Encoding:gzip,deflate,sdch’;

PDF Merger for PHP

https://pdfmerger.codeplex.com/

最近有个比较特殊的功能实现,需要处理html生成指定的pdf数据文件,通时需要拼接各种法律文件。原来通过图片,主要复制不方便,且图片进行压缩时,有色块干扰。而且pdf转html再转成pdf,会出现各种奇怪的问题。所以考虑通过合并多个pdf文件进行解决。

<?php
include 'PDFMerger.php';

$pdf = new PDFMerger;

$pdf->addPDF('samplepdfs/one.pdf', '1, 3, 4')
	->addPDF('samplepdfs/two.pdf', '1-2')
	->addPDF('samplepdfs/three.pdf', 'all')
	->merge('file', 'samplepdfs/TEST2.pdf');

	//REPLACE 'file' WITH 'browser', 'download', 'string', or 'file' for output options
	//You do not need to give a file path for browser, string, or download - just the name.
?>