自考 高等数学学习工具

https://www.shuxuele.com/index.html

涵盖初中数学物理到大学大概大专(相当于高等数学工专前几章的大概内容)

http://www.fhdq.net/sx/14.html

各类数学符号发音

https://www.geogebra.org/classic

空间图像绘制

https://zs.symbolab.com/solver/

超级计算器,部分答案需要付费,不过已经可以大概猜到结果了

需要相关pdf的私下~

高等数学

自考相关

目前看一科找一科.

2019-07 半年份的懒人笔记

米波现场调整为 win 版本
    不过win下,会牺牲不少性能,如果要求不高,可以考虑
修复微擎 在php 7.2 环境下登录异常
    mbstring.fun_overload 导致各种字符串函数异常,导致session解密异常
php 7.2以上版本安装跳过ssl协议
pecl install channel://pecl.php.net/mcrypt-1.0.1
workerman 模拟压力测试脚本
httponly
    chrome 特性,只有返回时,标记为httponly,之后会无法在浏览器端读取以及修改.且发送的httponly不会再出出标记
nmap-vulners和vulscan 
    https://www.4hou.com/technology/10481.html 
    需要走代理更新库
        shell 使用 shadowsockets 局域网代理
        curl 才能测试 ping 和 telnet 需要单独安装代理或shadowsock
服务器备份
    svn 直接复制
        passwd,authz
    mysql 复制数据库后,表空间要一起复制.
对源码做特殊修改,使include 不被open_basedir所限制, 新人测试环境,保护源码安全
    php 源码 main/fopen_wrappers.c (代码部分是复制别人的...哈哈...省好多时间)
        源码修改测试, 有 ./configure --disable-all 方便快速编译,只针对测试功能测试
        PHPAPI int php_check_open_basedir_ex(const char *path, int warn TSRMLS_DC)
    php 源码编译安装
        直接复制lnmp phpinfo中的编译参数,避免出错
        collect2: ld returned 1 exit status make: *** [sapi/cli/php] Error 1
        解决办法:make ZEND_EXTRA_LIBS='-liconv'
        ln -s /usr/local/lib/libiconv.so.2 /usr/lib64/
    独立php版本实现
        需要另外指定编译目录,配置独立端口,配置独立 /tmp/phpxxx.sock,同时nginx也需要做同样处理,用alias会方便很多.
            alias 全局修改 /etc/bashrc (注意空格)
samba
    无法访问...可能没有权限...这句不关键,第二句才是关键..
    Samba报错:不允许一个用户使用一个以上用户名与服务器或共享资源的多重连接
sqlserver php
    https://www.jianshu.com/p/245b14583c47
    只有7.0正常,7.2还不行.
win 分钟级php类计划任务
    php -q xxx.php
cups https://www.cups.org/
"操作无法完成(错误 0X00000709)。再次检查打印机名称、并确保打印机连接网络"
    识别不到打印机驱动问题. 可下载目标打印机驱动exe ,右键直接解压,再通过查找安装找到目标文件
    xp识货无法安装的问题.只能随便添加一个打印机(此时无效),再直接修改注册表,改成目标打印机
    打印机
        HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Print/Printers
    打印驱动
        HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Print/Environments/Windows NT x86/Drivers
apache vhost单独配置php配置参数
     php_admin_value open_basedir xxx
wkhtmltopdf 打印A4
    隐藏头,隐藏尾(页面宽度750左右为A4常规大小)
    wkhtmltopdf -T 0 -L 0 -B 0 --page-size A4 --print-media-type --disable-smart-shrinking http://xxx  xxx.pdf
中文分词
    mysql 5.7 以上,同时设置ngram_token_size=2,200w内,一次like且文本不是很长的情况下,可以不考虑用like.多次like的情况下,10w起就需要用分词了,速度下降得比较厉害.
mysql 一次命令复制表以及数据
    CREATE TABLE igd_sysmsg_backup AS   (   SELECT *   FROM igd_sysmsg   )

 

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 业务并发测试

<?php
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;
use \Workerman\Connection\AsyncTcpConnection;
use \Workerman\Lib\Timer;

require_once './vendor/autoload.php';
$worker = new Worker ();
$worker->onWorkerStart = 'connect';
function connect() {
	// 2000个链接
	// if ($count ++ >= 400) {
	// return;
	// }
	for($count = 0; $count < 400; $count ++) {
		$func = function ($count) {
			$session_id = 'd34pkfuf88vom9ugptop7e5' . $count;
			// 建立异步链接
			// $con = new AsyncTcpConnection ( 'ws://xxxx:8282' );
			$con = new AsyncTcpConnection ( 'ws://xxxxx:8282' );
			$con->onMessage = function ($con, $msg) use ($session_id, $count) {
				// 服务器消息推送
				echo "recv $session_id $msg\n";
				$msg = json_decode ( $msg, true );
				if ($msg && isset ( $msg ['action'] )) {
					switch ($msg ['action']) {
						case 'broadcast' :
							$time = ( int ) date ( 'dHis', time () );
							$con->send ( "{\"model\":\"__workerman\",\"action\":\"set_value\",\"value_key\":\"client_time\",\"value_value\":{$time}}" );
							break;
					}
				}
			};
			$con->onClose = function ($con) use ($session_id, $count) {
				echo "con $session_id close\n";
			};
			$con->onConnect = function ($con) use ($session_id, $count) {
				// $con->send ( "{\"model\":\"__workerman\",\"action\":\"set_value\",\"value_key\":\"score\",\"value_value\":{$val}}" );
				// 模拟登录动作
				$con->send ( "{\"model\":\"remote\",\"action\":\"msg\",\"remote_url\":\"http://xxxxxx/workerman_remote/do/\",\"params\":{\"model\":\"user\",\"action\":\"login\",\"rm_session_id\":\"{$session_id}\"}}" );
				// 当前链接每10秒发个心跳包
				$time = ( int ) date ( 'dHis', time () );
				$con->send ( "{\"model\":\"__workerman\",\"action\":\"set_value\",\"value_key\":\"client_time\",\"value_value\":{$time}}" );
				Timer::add ( 10, function () use ($con, $session_id, $count, $time) {
					// 每10秒推送一个消息
					// $con->send ( "{\"model\":\"user\",\"action\":\"sys_time\",\"session_id\":\"{$session_id}\",\"client_time\":\"{$time}\"}" );
					// 模拟数钱,一秒推送10个消息
					for($i = 0; $i < 10; $i ++) {
						$val = ( int ) date ( 'dHis', time () );
						$con->send ( "{\"model\":\"__workerman\",\"action\":\"set_value\",\"value_key\":\"score\",\"value_value\":{$val}}" );
					}
					echo $count . " send complete\n";
				} );
			};
			$con->connect ();
			echo $count . " connections complete\n";
		};
		$func ( $count );
	}
}
Worker::runAll ();

1

2

在400条的情况下,内存用不了多少,稳定性也有保证,不过实际情况,还是得具体场景才能判断.

小程序web-view开发框架(写给公司的特殊形式,只做说明,不含代码~.~)

需要先了解手机软件开发有三种形式,原生,混合开发,React-Native开发:

原生开发就是使用手机所支持的原生语言进行开发,像android是用java,iphone是 Objective-C

混合开发就是内置web-view形式,通过在web-view中注入一个js对象,并提供原生语言接口,js通过调用这些接口,实现网页应用和手机间的交互,像phonegap,dcloud等,

React-Native 开发则是直接创建第三种语言,类似html和js,然后生成对应的手机软件

三者主要对比性能和开发便捷程度

原生速度最快,但是跨平台性限制,导致各平台,都需要开发对应版本, 所以开发速度肯定是最慢的.

混合开发,各平台提供统一的接口给注入的js对象调用,所以只需要掌握基本的html和js基础,熟悉注入对象的文档即可开发能在多平台上跑的应用.速度最快,但是性能是最差的.因为是通过web-view进行渲染,性能是个硬伤,但是mui等前端框架,可极大程度的缩小差距. 但是混合应用, 说到底,只能开发展示类的应用.

React-Native 则是编写完成后,编辑器会编译成对应平台的软件,性能相对来说比混合好,开发也相对中等.

小程序则是比较接近react-native的形式,但是又可通过web-view的处理形式实现混合开发,公司这边使用的是web-view形式,上手相对容易很多.

 

小程序的限制:

小程序的限制是底层权限没有开放,限制比较高,导致写出来的应用,实际上是各公众号的功能差不多.在网速正常情况下,二者功能基本没有太大差别.唯一的优势基本就是入口可以在顶部下来访问.但是最大的缺点则是无法自由的进行模板消息推送. 小程序只有提交表单的情况下,会得到一个表单id,通过该表单才可以进行小程序消息下发,所以小程序一但离线,基本无法通知用户, 只能做为一款极其轻量的应用. 不过在绑定的前提下,公众号可以代替小程序推送消息,并通过消息访问小程序,前提是关注. 做到这步非常麻烦.小程序返回公众号流程繁琐,无法直接进入公众号.

 

公众号的类型:订阅号,服务号,小程序,企业号…

各公众号openid相互独立,所以,小程序用户的openid不同于其它公众号,且无法共享

公司web-view形式的小程序开发:

微信开发者工具

https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

框架目录结构,每种必需小程序的功能,单独写成一个独立页面,然后通过wx进行访问

24

小程序开发者工具截图

1

小程序获取用户信息不同于公众号授权跳转,进入的时候就强制授权获取,且只能获取一次.且返回的是用户的详细信息和一个code,通过该code,才能获取小程序的用户openid. 所以入口处将用户信息传递到服务器端,必须由服务器进行保存,如果丢失,二次访问时,像头像,昵称这些信息将无法获取.除非将对应服务号接入,通过服务号进行授权跳转(小程序web-view本质是个浏览器,只是限制了长按扫描二维码,但是授权跳转有效)

2

download (2)

小程序由于分享,支付,客服,关注,登录等特殊动作,必须由纯小程序代码进行编写,所以当需要触发上述功能时,web-view通过内置对象,携带参数并跳转到对应的页面,操作完成后,进行返回.以上为公司小程序的基本实现形式.

3

特殊模板小程序开发

通过开发一套完整的小程序,并提交到开放平台,绑定做为通用小程序模板, 这样只需要用户给对应的平台授权,平台可以直接给对应用户生成一套专属的小程序. 通过配置扩展变量,标记当前是哪个用户使用该模板.

4

web-view的最大优势为修改在web端,无需像小程序一般提交修改审核,极其便利.同时,在确保公众号开发一套任意的功能,通过关联openid,即可无缝整合成小程序访问.

最近小程序的优化,一个是通过头部跳转 Location: xxx 跳转”实现”不跳转太多次进入页面