首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
183 阅读
2
php接口优化 使用curl_multi_init批量请求
144 阅读
3
《从菜鸟到大师之路 ElasticSearch 篇》
107 阅读
4
2024年备考系统架构设计师
104 阅读
5
PHP 文件I/O
92 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
786
篇文章
累计收到
31
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
560
篇与
的结果
2023-08-08
PHP引用(&)使用详解
PHP引用(&)使用详解摘自:PHP引用(&)使用详解官方文档:1.引用是什么:http://www.php.net/manual/zh/language.references.whatare.php2.引用做什么:http://www.php.net/manual/zh/language.references.whatdo.php3.引用传递:http://www.php.net/manual/zh/language.references.pass.php4.引用返回:http://www.php.net/manual/zh/language.references.return.phpphp的引用(就是在变量或者函数、对象等前面加上&符号)在PHP 中引用的意思是:不同的名字访问同一个变量内容。与C语言中的指针是有差别的.C语言中的指针里面存储的是变量的内容,在内存中存放的地址。1.变量的引用PHP 的引用允许你用两个变量来指向同一个内容复制代码<?$a="ABC";$b =&$a;echo $a;//这里输出:ABCecho $b;//这里输出:ABC$b="EFG";echo $a;//这里$a的值变为EFG 所以输出EFGecho $b;//这里输出EFG?>复制代码2.函数的引用传递(传址调用)传址调用我就不多说了 下面直接给出代码复制代码<?phpfunction test(&$a){$a=$a+100;}$b=1;echo $b;//输出1test($b); //这里$b传递给函数的其实是$b的变量内容所处的内存地址,通过在函数里改变$a的值 就可以改变$b的值了echo "";echo $b;//输出101?>复制代码要注意的是,在这里test(1);的话就会出错,原因自己去想。注意:上面的“ test($b); ” 中的$b前面不要加 & 符号,但是在函数“call_user_func_array”中,若要引用传参,就得需要 & 符号,如下代码所示:复制代码<?phpfunction a(&$b){$b++;}$c=0;call_user_func_array('a',array(&$c));echo $c;//输出 1?>复制代码3.函数的引用返回先看代码复制代码<?phpfunction &test(){static $b=0;//申明一个静态变量$b=$b+1;echo $b;return $b;}$a=test();//这条语句会输出 $b的值 为1$a=5;$a=test();//这条语句会输出 $b的值 为2$a=&test();//这条语句会输出 $b的值 为3$a=5;$a=test();//这条语句会输出 $b的值 为6?>复制代码下面解释下:通过这种方式$a=test();得到的其实不是函数的引用返回,这跟普通的函数调用没有区别 至于原因: 这是PHP的规定PHP规定通过$a=&test(); 方式得到的才是函数的引用返回至于什么是引用返回呢(PHP手册上说:引用返回用在当想用函数找到引用应该被绑定在哪一个变量上面时。) 这句狗屁话 害我半天没看懂用上面的例子来解释就是$a=test()方式调用函数,只是将函数的值赋给$a而已, 而$a做任何改变 都不会影响到函数中的$b而通过$a=&test()方式调用函数呢, 他的作用是 将return $b中的 $b变量的内存地址与$a变量的内存地址 指向了同一个地方即产生了相当于这样的效果($a=&$b;) 所以改变$a的值 也同时改变了$b的值 所以在执行了$a=&test();$a=5;以后,$b的值变为了5这里是为了让大家理解函数的引用返回才使用静态变量的,其实函数的引用返回多用在对象中另附一个php官方例子:复制代码This is the way how we use pointer to access variable inside the class.<?phpclass talker{private $data = 'Hi';public function & get(){return $this->data;}public function out(){echo $this->data;}}$aa = new talker();$d = &$aa->get();$aa->out();$d = 'How';$aa->out();$d = 'Are';$aa->out();$d = 'You';$aa->out();?>the output is "HiHowAreYou"复制代码4.对象的引用复制代码<?phpclass a{var $abc="ABC";}$b=new a;$c=$b;echo $b->abc;//这里输出ABCecho $c->abc;//这里输出ABC$b->abc="DEF";echo $c->abc;//这里输出DEF?>复制代码以上代码是在PHP5中的运行效果在PHP5中 对象的赋值是个引用的过程。上列中$b=new a; $c=$b; 其实等效于$b=new a; $c=&$b;PHP5中默认就是通过引用来调用对象, 但有时你可能想建立一个对象的副本,并希望原来的对象的改变不影响到副本 . 为了这样的目的,PHP5定义了一个特殊的方法,称为__clone。自 PHP 5 起,new 自动返回引用,因此在此使用 =& 已经过时了并且会产生 E_STRICT 级别的消息。在php4中,对象的赋值是个拷贝过程,如:$b=new a,其中new a产生的是一个匿名的a对象实例,而此时的$b是对这个匿名对象的拷贝。同理$c=$b,也是对$b内容的一个拷贝。所以在php4中,为了节省内存空间,$b=new a 一般会改成引用的模式,即 $b=& new a。下面再来个 官方 提供的例子:在php5中,你不需要额外添加什么东西就可到达“对象引用”的功能:复制代码<?phpclass foo{protected $name;function __construct($str){$this->name = $str;}function __toString(){return 'my name is "'. $this->name .'" and I live in "' . CLASS . '".' . "\n";}function setName($str){$this->name = $str;}}class MasterOne{protected $foo;function __construct($f){$this->foo = $f;}function __toString(){return 'Master: ' . CLASS . ' | foo: ' . $this->foo . "\n";}function setFooName($str){$this->foo->setName( $str );}}class MasterTwo{protected $foo;function __construct($f){$this->foo = $f;}function __toString(){return 'Master: ' . CLASS . ' | foo: ' . $this->foo . "\n";}function setFooName($str){$this->foo->setName( $str );}}$bar = new foo('bar');print("\n");print("Only Created $bar and printing $bar\n");print( $bar );print("\n");print("Now $baz is referenced to $bar and printing $bar and $baz\n");$baz =& $bar;print( $bar );print("\n");print("Now Creating MasterOne and Two and passing $bar to both constructors\n");$m1 = new MasterOne( $bar );$m2 = new MasterTwo( $bar );print( $m1 );print( $m2 );print("\n");print("Now changing value of $bar and printing $bar and $baz\n");$bar->setName('baz');print( $bar );print( $baz );print("\n");print("Now printing again MasterOne and Two\n");print( $m1 );print( $m2 );print("\n");print("Now changing MasterTwo's foo name and printing again MasterOne and Two\n");$m2->setFooName( 'MasterTwo\'s Foo' );print( $m1 );print( $m2 );print("Also printing $bar and $baz\n");print( $bar );print( $baz );?>复制代码输出:复制代码Only Created $bar and printing $barmy name is "bar" and I live in "foo".Now $baz is referenced to $bar and printing $bar and $bazmy name is "bar" and I live in "foo".Now Creating MasterOne and Two and passing $bar to both constructorsMaster: MasterOne | foo: my name is "bar" and I live in "foo".Master: MasterTwo | foo: my name is "bar" and I live in "foo".Now changing value of $bar and printing $bar and $bazmy name is "baz" and I live in "foo".my name is "baz" and I live in "foo".Now printing again MasterOne and TwoMaster: MasterOne | foo: my name is "baz" and I live in "foo".Master: MasterTwo | foo: my name is "baz" and I live in "foo".Now changing MasterTwo's foo name and printing again MasterOne and TwoMaster: MasterOne | foo: my name is "MasterTwo's Foo" and I live in "foo".Master: MasterTwo | foo: my name is "MasterTwo's Foo" and I live in "foo".Also printing $bar and $bazmy name is "MasterTwo's Foo" and I live in "foo".my name is "MasterTwo's Foo" and I live in "foo".复制代码上个例子解析:$bar = new foo('bar');$m1 = new MasterOne( $bar );$m2 = new MasterTwo( $bar );实例对象$m1与$m2中的$bar是对实例$bar的引用,而非拷贝,这是php5中,对象引用的特点,也就是说1.$m1或$m2内部,任何对$bar的操作都会影响外部对象实例$bar的相关值。2.外部对象实例$bar的改变也会影响$m1和$m2内部的$bar的引用相关值。在php4中,要实现如上述的 用一个对象实例去当着另外一个对象的属性时,其等价代码(即引用调用)类似如下:class foo{var $bar;function setBar(&$newBar){$this->bar =& newBar;}}5.引用的作用如果程序比较大,引用同一个对象的变量比较多,并且希望用完该对象后手工清除它,个人建议用 "&" 方式,然后用$var=null的方式清除. 其它时候还是用php5的默认方式吧. 另外, php5中对于大数组的传递,建议用 "&" 方式, 毕竟节省内存空间使用。6.取消引用当你 unset 一个引用,只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了。例如:<?php$a = 1;$b =& $a;unset ($a);?>不会 unset $b,只是 $a。7.global 引用当用 global $var 声明一个变量时实际上建立了一个到全局变量的引用。也就是说和这样做是相同的:<?php$var =& $GLOBALS["var"];?>这意味着,例如,unset $var 不会 unset 全局变量。如果在一个函数内部给一个声明为 global 的变量赋于一个引用,该引用只在函数内部可见。可以通过使用 $GLOBALS 数组避免这一点。Example 在函数内引用全局变量复制代码<?php$var1 = "Example variable";$var2 = "";function global_references($use_globals){global $var1, $var2;if (!$use_globals) {$var2 =& $var1; // visible only inside the function} else {$GLOBALS["var2"] =& $var1; // visible also in global context}}global_references(false);echo "var2 is set to '$var2'\n"; // var2 is set to ''global_references(true);echo "var2 is set to '$var2'\n"; // var2 is set to 'Example variable'?>复制代码把 global $var; 当成是 $var =& $GLOBALS['var']; 的简写。从而将其它引用赋给 $var 只改变了本地变量的引用。8.$this在一个对象的方法中,$this 永远是调用它的对象的引用。//下面再来个小插曲php中对于地址的指向(类似指针)功能不是由用户自己来实现的,是由Zend核心实现的,php中引用采用的是“写时拷贝”的原理,就是除非发生写操作,指向同一个地址的变量或者对象是不会被拷贝的。通俗的讲1:如果有下面的代码<?$a="ABC";$b=&$a;?>其实此时 $a与$b都是指向同一内存地址 而并不是$a与$b占用不同的内存2:如果在上面的代码基础上再加上如下代码<?php$a="EFG";?>由于$a与$b所指向的内存的数据要重新写一次了,此时Zend核心会自动判断 自动为$b生产一个$a的数据拷贝,重新申请一块内存进行存储php的引用(就是在变量或者函数、对象等前面加上&符号)是个高级话题,新手多注意,正确的理解php的引用很重要,对性能有较大影响,而且理解错误可能导致程序错误!很 多人误解php中的引用跟C当中的指针一样,事实上并非如此,而且很大差别。C语言中的指针除了在数组传递过程中不用显式申明外,其他都需要使用*进行定 义,而php中对于地址的指向(类似指针)功能不是由用户自己来实现的,是由Zend核心实现的,php中引用采用的是“写时拷贝”的原理,就是除非发生 写操作,指向同一个地址的变量或者对象是不会被拷贝的,比如下面的代码:$a = array('a','c'...'n');$b = $a;如 果程序仅执行到这里,$a和$b是相同的,但是并没有像C那样,$a和$b占用不同的内存空间,而是指向了同一块内存,这就是php和c的差别,并不需要 写成$b=&$a才表示$b指向$a的内存,zend就已经帮你实现了引用,并且zend会非常智能的帮你去判断什么时候该这样处理,什么时候不 该这样处理。如果在后面继续写如下代码,增加一个函数,通过引用的方式传递参数,并打印输出数组大小。function printArray(&$arr) //引用传递{print(count($arr));}printArray($a);上面的代码中,我们通过引用把$a数组传入printArray()函数,zend引擎会认为printArray()可能会导致对$a的改变,此时就会自动为$b生产一个$a的数据拷贝,重新申请一块内存进行存储。这就是前面提到的“写时拷贝”概念。如果我们把上面的代码改成下面这样:function printArray($arr) //值传递{print(count($arr));}printArray($a);上面的代码直接传递$a值到printArray()中,此时并不存在引用传递,所以没有出现写时拷贝。大家可以测试一下上面两行代码的执行效率,比如外面加入一个循环1000次,看看运行的耗时,结果会让你知道不正确使用引用会导致性能下降30%以上。自我理解:按传值的话是与函数内的参数无关,相当于局部变量的作用,而按传址(引用)的话却与函数内的参数有关,相当于全局变量的作用.而从性能方面来说,看上面分析就够..摘自:http://www.php.net/manual/zh/language.references.whatdo.php引用做什么PHP 的引用允许用两个变量来指向同一个内容。意思是,当这样做时:<?php$a =& $b;?>这意味着 $a 和 $b 指向了同一个变量。Note:$a 和 $b 在这里是完全相同的,这并不是 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一个地方。Note:如果具有引用的数组被拷贝,其值不会解除引用。对于数组传值给函数也是如此。Note:如果对一个未定义的变量进行引用赋值、引用参数传递或引用返回,则会自动创建该变量。Example #1 对未定义的变量使用引用<?phpfunction foo(&$var) { }foo($a); // $a is "created" and assigned to null$b = array();foo($b['b']);var_dump(array_key_exists('b', $b)); // bool(true)$c = new StdClass;foo($c->d);var_dump(property_exists($c, 'd')); // bool(true)?>同样的语法可以用在函数中,它返回引用,以及用在 new 运算符中(PHP 4.0.4 以及以后版本):<?php$bar =& new fooclass();$foo =& find_var($bar);?>自 PHP 5 起,new 自动返回引用,因此在此使用 =& 已经过时了并且会产生 E_STRICT 级别的消息。Note:不用 & 运算符导致对象生成了一个拷贝。如果在类中用 $this,它将作用于该类当前的实例。没有用 & 的赋值将拷贝这个实例(例如对象)并且 $this 将作用于这个拷贝上,这并不总是想要的结果。由于性能和内存消耗的问题,通常只想工作在一个实例上面。尽管可以用 @ 运算符来抑制构造函数中的任何错误信息,例如用 @new,但用 &new 语句时这不起效果。这是 Zend 引擎的一个限制并且会导致一个解析错误。Warning如果在一个函数内部给一个声明为 global 的变量赋于一个引用,该引用只在函数内部可见。可以通过使用 $GLOBALS 数组避免这一点。Example #2 在函数内引用全局变量<?php$var1 = "Example variable";$var2 = "";function global_references($use_globals){global $var1, $var2;if (!$use_globals) {$var2 =& $var1; // visible only inside the function} else {$GLOBALS["var2"] =& $var1; // visible also in global context}}global_references(false);echo "var2 is set to '$var2'\n"; // var2 is set to ''global_references(true);echo "var2 is set to '$var2'\n"; // var2 is set to 'Example variable'?>把 global $var; 当成是 $var =& $GLOBALS['var']; 的简写。从而将其它引用赋给 $var 只改变了本地变量的引用。Note:如果在 foreach 语句中给一个具有引用的变量赋值,被引用的对象也被改变。Example #3 引用与 foreach 语句<?php$ref = 0;$row =& $ref;foreach (array(1, 2, 3) as $row) {// do something}echo $ref; // 3 - last element of the iterated array?>引用做的第二件事是用引用传递变量。这是通过在函数内建立一个本地变量并且该变量在呼叫范围内引用了同一个内容来实现的。例如:<?phpfunction foo(&$var){$var++;}$a=5;foo($a);?>将使 $a 变成 6。这是因为在 foo 函数中变量 $var 指向了和 $a 指向的同一个内容。更多详细解释见引用传递。引用做的第三件事是引用返回。数组引用的一个bug(后来仔细推敲,其实不是bug)摘自:http://www.php.net/manual/zh/language.references.whatdo.phpIt appears that references can have side-effects. Below are two examples. Both are simply copying one array to another. In the second example, a reference is made to a value in the first array before the copy. In the first example the value at index 0 points to two separate memory locations. In the second example, the value at index 0 points to the same memory location.I won't say this is a bug, because I don't know what the designed behavior of PHP is, but I don't think ANY developers would expect this behavior, so look out.An example of where this could cause problems is if you do an array copy in a script and expect on type of behavior, but then later add a reference to a value in the array earlier in the script, and then find that the array copy behavior has unexpectedly changed.复制代码<?php// Example one$arr1 = array(1);echo "\nbefore:\n";echo "$arr1[0] == {$arr1[0]}\n";$arr2 = $arr1;$arr2[0]++;echo "\nafter:\n";echo "$arr1[0] == {$arr1[0]}\n";echo "$arr2[0] == {$arr2[0]}\n";输出:before:$arr1[0] == 1after:$arr1[0] == 1$arr2[0] == 2// Example two$arr3=array(1);$a=&$arr3[0];echo"\nbefore:\n";echo"$a == $a\n";echo"$arr3[0] == {$arr3[0]}\n";$arr4=$arr3;$arr4[0]++;echo"\nafter:\n";echo"$a == $a\n";echo"$arr3[0] == {$arr3[0]}\n";echo"$arr4[0] == {$arr4[0]}\n";输出:before:$a == 1$arr3[0] == 1after:$a == 2$arr3[0] == 2$arr4[0] == 2?>复制代码分析说明:对于“Example two”,刚开始还以为是个bug,其实仔细推敲,非也,分析如下,在赋值(拷贝)$arr4=$arr3;之前,还有个对$arr3的第一个元素建立引用的过程,即$a=&$arr3[0];所以在后来的赋值拷贝( $arr4=$arr3; ),会把这个引用一并拷贝过去,所以说$a、$arr3[0]、$arr4[0] 三者其实是引用关系,指向同一个地方。
2023年08月08日
15 阅读
0 评论
0 点赞
2023-08-08
查找附近的xxx 球面距离以及Geohash方案探讨
查找附近的xxx 球面距离以及Geohash方案探讨随着移动终端的普及,很多应用都基于LBS功能,附近的某某(餐馆、银行、妹纸等等)。基础数据中,一般保存了目标位置的经纬度;利用用户提供的经纬度,进行对比,从而获得是否在附近。目标:查找附近的XXX,由近到远返回结果,且结果中有与目标点的距离。针对查找附近的XXX,提出两个方案,如下:一、方案A:抽象为球面两点距离的计算,即已知道球面上两点的经纬度;点(纬度,经度),A($radLat1,$radLng1)、B($radLat2,$radLng2);优点:通俗易懂,部署简单便捷缺点:每次都会查询数据库,性能堪忧1、推导通过余弦定理以及弧度计算方法,最终推导出来的算式A为:1$s = acos(cos($radLat1)cos($radLat2)*cos($radLng1-$radLng2)+sin($radLat1)sin($radLat2))*$R;目前网上大多使用Google公开的距离计算公司,推导算式B为:1$s = 2*asin(sqrt(pow(sin(($radLat1-$radLat2)/2),2)+cos($radLat1)*cos($radLat2)*pow(sin(($radLng1-$radLng2)/2),2)))*$R;其中 :$radLat1、$radLng1,$radLat2,$radLng2 为弧度$R 为地球半径2、通过测试两种算法,结果相同且都正确,但通过PHP代码测试,两点间距离,10W次性能对比,自行推导版本计算时长算式B较优,如下://算式A0.56368780136108float(431)0.57460689544678float(431)0.59051203727722float(431)//算式B0.47404885292053float(431)0.47808718681335float(431)0.47946381568909float(431)3、所以采用数学方法推导出的公式:123456789101112131415161718192021222324<?php//根据经纬度计算距离 其中A($lat1,$lng1)、B($lat2,$lng2) public static function getDistance($lat1,$lng1,$lat2,$lng2) { //地球半径 $R = 6378137; //将角度转为狐度 $radLat1 = deg2rad($lat1); $radLat2 = deg2rad($lat2); $radLng1 = deg2rad($lng1); $radLng2 = deg2rad($lng2); //结果 $s = acos(cos($radLat1)*cos($radLat2)*cos($radLng1-$radLng2)+sin($radLat1)*sin($radLat2))*$R; //精度 $s = round($s* 10000)/10000; return round($s); } ?>4、在实际应用中,需要从数据库中遍历取出符合条件,以及排序等操作,将所有数据取出,然后通过PHP循环对比,筛选符合条件结果,显然性能低下;所以我们利用下Mysql存储函数来解决这个问题吧。4.1、创建Mysql存储函数,并对经纬度字段建立索引12345678910111213141516171819202122232425262728293031323334353637383940414243DELIMITER $$CREATE DEFINER=root@% FUNCTION GETDISTANCE(lat1 DOUBLE, lng1 DOUBLE, lat2 DOUBLE, lng2 DOUBLE) RETURNS doubleREADS SQL DATADETERMINISTICBEGINDECLARE RAD DOUBLE;DECLARE EARTH_RADIUS DOUBLE DEFAULT 6378137;DECLARE radLat1 DOUBLE;DECLARE radLat2 DOUBLE;DECLARE radLng1 DOUBLE;DECLARE radLng2 DOUBLE;DECLARE s DOUBLE;SET RAD = PI() / 180.0;SET radLat1 = lat1 * RAD;SET radLat2 = lat2 * RAD;SET radLng1 = lng1 * RAD;SET radLng2 = lng2 * RAD;SET s = ACOS(COS(radLat1)COS(radLat2)COS(radLng1-radLng2)+SIN(radLat1)SIN(radLat2))EARTH_RADIUS;SET s = ROUND(s * 10000) / 10000;RETURN s;END$$DELIMITER ;4.2、查询SQL通过SQL,可设置距离以及排序;可搜索出符合条件的信息,以及有一个较好的排序1SELECT *,latitude,longitude,GETDISTANCE(latitude,longitude,30.663262,104.071619) AS distance FROM mb_shop_ext where 1 HAVING distance<1000 ORDER BY distance ASC LIMIT 0,10二、方案BGeohash算法;geohash是一种地址编码,它能把二维的经纬度编码成一维的字符串。比如,成都永丰立交的编码是wm3yr31d2524优点:1、利用一个字段,即可存储经纬度;搜索时,只需一条索引,效率较高2、编码的前缀可以表示更大的区域,查找附近的,非常方便。 SQL中,LIKE ‘wm3yr3%’,即可查询附近的所有地点。3、通过编码精度可模糊坐标、隐私保护等。缺点: 距离和排序需二次运算(筛选结果中运行,其实挺快)1、geohash的编码算法成都永丰立交经纬度(30.63578,104.031601)1.1、纬度范围(-90, 90)平分成两个区间(-90, 0)、(0, 90), 如果目标纬度位于前一个区间,则编码为0,否则编码为1。由于30.625265属于(0, 90),所以取编码为1。然后再将(0, 90)分成 (0, 45), (45, 90)两个区间,而39.92324位于(0, 45),所以编码为0,然后再将(0, 45)分成 (0, 22.5), (22.5, 45)两个区间,而39.92324位于(22.5, 45),所以编码为1,依次类推可得永丰立交纬度编码为101010111001001000100101101010。1.2、经度也用同样的算法,对(-180, 180)依次细分,(-180,0)、(0,180) 得出编码1100100111111010011000000000001.3、合并经纬度编码,从高到低,先取一位经度,再取一位纬度;得出结果 1110010011000111111010111000110000101100000100010100010001001.4、用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,得到(30.63578,104.031601)的编码为wm3yr31d2524。12345611100 10011 00011 11110 10111 00011 00001 01100 00010 00101 00010 00100 => wm3yr31d2524十进制 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15base32 0 1 2 3 4 5 6 7 8 9 b c d e f g十进制 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31base32 h j k m n p q r s t u v w x y z2、策略1、在纬度和经度入库时,数据库新加一字段geohash,记录此点的geohash值2、查找附近,利用 在SQL中 LIKE ‘wm3yr3%’;且此结果可缓存;在小区域内,不会因为改变经纬度,而重新数据库查询3、查找出的有限结果,如需要求距离或者排序,可利用距离公式和二维数据排序;此时也是少量数据,会很快的。3、PHP基类geohash.class.php123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171<?php/**Encode and decode geohashes**/class Geohash{private $coding="0123456789bcdefghjkmnpqrstuvwxyz"; private $codingMap=array(); public function Geohash() { for($i=0; $i<32; $i++) { $this->codingMap[substr($this->coding,$i,1)]=str_pad(decbin($i), 5, "0", STR_PAD_LEFT); } } public function decode($hash) { $binary=""; $hl=strlen($hash); for($i=0; $i<$hl; $i++) { $binary.=$this->codingMap[substr($hash,$i,1)]; } $bl=strlen($binary); $blat=""; $blong=""; for ($i=0; $i<$bl; $i++) { if ($i%2) $blat=$blat.substr($binary,$i,1); else $blong=$blong.substr($binary,$i,1); } $lat=$this->binDecode($blat,-90,90); $long=$this->binDecode($blong,-180,180); $latErr=$this->calcError(strlen($blat),-90,90); $longErr=$this->calcError(strlen($blong),-180,180); $latPlaces=max(1, -round(log10($latErr))) - 1; $longPlaces=max(1, -round(log10($longErr))) - 1; $lat=round($lat, $latPlaces); $long=round($long, $longPlaces); return array($lat,$long); } public function encode($lat,$long) { $plat=$this->precision($lat); $latbits=1; $err=45; while($err>$plat) { $latbits++; $err/=2; } $plong=$this->precision($long); $longbits=1; $err=90; while($err>$plong) { $longbits++; $err/=2; } $bits=max($latbits,$longbits); $longbits=$bits; $latbits=$bits; $addlong=1; while (($longbits+$latbits)%5 != 0) { $longbits+=$addlong; $latbits+=!$addlong; $addlong=!$addlong; } $blat=$this->binEncode($lat,-90,90, $latbits); $blong=$this->binEncode($long,-180,180,$longbits); $binary=""; $uselong=1; while (strlen($blat)+strlen($blong)) { if ($uselong) { $binary=$binary.substr($blong,0,1); $blong=substr($blong,1); } else { $binary=$binary.substr($blat,0,1); $blat=substr($blat,1); } $uselong=!$uselong; } $hash=""; for ($i=0; $i<strlen($binary); $i+=5) { $n=bindec(substr($binary,$i,5)); $hash=$hash.$this->coding[$n]; } return $hash; } private function calcError($bits,$min,$max) { $err=($max-$min)/2; while ($bits--) $err/=2; return $err; } private function precision($number) { $precision=0; $pt=strpos($number,'.'); if ($pt!==false) { $precision=-(strlen($number)-$pt-1); } return pow(10,$precision)/2; } private function binEncode($number, $min, $max, $bitcount) { if ($bitcount==0) return ""; $mid=($min+$max)/2; if ($number>$mid) return "1".$this->binEncode($number, $mid, $max,$bitcount-1); else return "0".$this->binEncode($number, $min, $mid,$bitcount-1); } private function binDecode($binary, $min, $max) { $mid=($min+$max)/2; if (strlen($binary)==0) return $mid; $bit=substr($binary,0,1); $binary=substr($binary,1); if ($bit==1) return $this->binDecode($binary, $mid, $max); else return $this->binDecode($binary, $min, $mid); }}?>三、测试123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130<?phprequire_once('Mysql.class.php');require_once('geohash.class.php');//mysql$conf = array('host' => '127.0.0.1', 'port' => 3306, 'user' => 'root', 'password' => '123456', 'database' => 'mocube', 'charset' => 'utf8', 'persistent' => false);$mysql = new Db_Mysql($conf);$geohash=new Geohash;//经纬度转换成Geohash/*$sql = 'select shop_id,latitude,longitude from mb_shop_ext';$data = $mysql->queryAll($sql);foreach($data as $val){$geohash_val = $geohash->encode($val['latitude'],$val['longitude']);$sql = 'update mb_shop_ext set geohash= "'.$geohash_val.'" where shop_id = '.$val['shop_id'];echo $sql;$re = $mysql->query($sql);var_dump($re);}*///获取附近的信息$n_latitude = $_GET['la'];$n_longitude = $_GET['lo'];//开始$b_time = microtime(true);//方案A,直接利用数据库存储函数,遍历排序/*$sql = 'SELECT *,latitude,longitude,GETDISTANCE(latitude,longitude,'.$n_latitude.','.$n_longitude.') AS distance FROM mb_shop_ext where 1 HAVING distance<1000 ORDER BY distance ASC';$data = $mysql->queryAll($sql);//结束$e_time = microtime(true);echo $e_time - $b_time;var_dump($data);exit;*///方案B geohash求出附近,然后排序//当前 geohash值$n_geohash = $geohash->encode($n_latitude,$n_longitude);//附近$n = $_GET['n'];$like_geohash = substr($n_geohash, 0, $n);$sql = 'select * from mb_shop_ext where geohash like "'.$like_geohash.'%"';echo $sql;$data = $mysql->queryAll($sql);//算出实际距离foreach($data as $key=>$val){$distance = getDistance($n_latitude,$n_longitude,$val['latitude'],$val['longitude']); $data[$key]['distance'] = $distance; //排序列 $sortdistance[$key] = $distance;}//距离排序array_multisort($sortdistance,SORT_ASC,$data);//结束$e_time = microtime(true);echo $e_time - $b_time;var_dump($data);//根据经纬度计算距离 其中A($lat1,$lng1)、B($lat2,$lng2)function getDistance($lat1,$lng1,$lat2,$lng2){//地球半径 $R = 6378137; //将角度转为狐度 $radLat1 = deg2rad($lat1); $radLat2 = deg2rad($lat2); $radLng1 = deg2rad($lng1); $radLng2 = deg2rad($lng2); //结果 $s = acos(cos($radLat1)*cos($radLat2)*cos($radLng1-$radLng2)+sin($radLat1)*sin($radLat2))*$R; //精度 $s = round($s* 10000)/10000; return round($s);}?>四、总结方案B的亮点在于:1、搜索结果可缓存,重复使用,不会因为用户有小范围的移动,直接穿透数据库查询。2、先缩小结果范围,再运算、排序,可提升性能。254条记录,性能对比,在实际应用场景中,方案B数据库搜索可内存缓存;且如数据量更大,方案B结果会更优。方案A:0.0165600776672360.0324029922485350.040318012237549方案B0.00798106193542480.00796699523925780.0064868927001953五、其他两种方案,根据应用场景以及负载情况合理选择,当然推荐方案B;不管哪种方案,都记得,给列加上索引,利于数据库检索。
2023年08月08日
9 阅读
0 评论
0 点赞
2023-08-07
Github上的PHP资源汇总大全
Github上的PHP资源汇总大全
2023年08月07日
16 阅读
0 评论
0 点赞
2023-08-07
PHP的执行原理/执行流程
PHP的执行原理/执行流程 更深入的学习和了解可以查看下面:风雨的博客http://www.laruence.com/2008/08/12/180.html百度研发中心的博客http://stblog.baidu-tech.com/?p=763王兴宾的博客http://blog.csdn.net/wanghao72214/article/details/3916825简介 先看看下面这个过程:我们从未手动开启过PHP的相关进程,它是随着Apache的启动而运行的;PHP通过mod_php5.so模块和Apache相连(具体说来是SAPI,即服务器应用程序编程接口);PHP总共有三个模块:内核、Zend引擎、以及扩展层;PHP内核用来处理请求、文件流、错误处理等相关操作;Zend引擎(ZE)用以将源文件转换成机器语言,然后在虚拟机上运行它;扩展层是一组函数、类库和流,PHP使用它们来执行一些特定的操作。比如,我们需要mysql扩展来连接MySQL数据库;当ZE执行程序时可能会需要连接若干扩展,这时ZE将控制权交给扩展,等处理完特定任务后再返还;最后,ZE将程序运行结果返回给PHP内核,它再将结果传送给SAPI层,最终输出到浏览器上。深入探讨 等等,没有这么简单。以上过程只是个简略版,让我们再深入挖掘一下,看看幕后还发生了些什么。Apache启动后,PHP解释程序也随之启动;PHP的启动过程有两步;第一步是初始化一些环境变量,这将在整个SAPI生命周期中发生作用;第二步是生成只针对当前请求的一些变量设置。PHP启动第一步 不清楚什么第一第二步是什么?别担心,我们接下来详细讨论一下。让我们先看看第一步,也是最主要的一步。要记住的是,第一步的操作在任何请求到达之前就发生了。启动Apache后,PHP解释程序也随之启动;PHP调用各个扩展的MINIT方法,从而使这些扩展切换到可用状态。看看php.ini文件里打开了哪些扩展吧;MINIT的意思是“模块初始化”。各个模块都定义了一组函数、类库等用以处理其他请求。 一个典型的MINIT方法如下:PHP_MINIT_FUNCTION(extension_name){/ Initialize functions, classes etc /}PHP启动第二步当一个页面请求发生时,SAPI层将控制权交给PHP层。于是PHP设置了用于回复本次请求所需的环境变量。同时,它还建立一个变量表,用来存放执行过程中产生的变量名和值。PHP调用各个模块的RINIT方法,即“请求初始化”。一个经典的例子是Session模块的RINIT,如果在php.ini中启用了Session模块,那在调用该模块的RINIT时就会初始化$_SESSION变量,并将相关内容读入;RINIT方法可以看作是一个准备过程,在程序执行之间就会自动启动。 一个典型的RINIT方法如下:PHP_RINIT_FUNCTION(extension_name) {/ Initialize session variables, pre-populate variables, redefine global variables etc /}PHP关闭第一步 如同PHP启动一样,PHP的关闭也分两步:一旦页面执行完毕(无论是执行到了文件末尾还是用exit或die函数中止),PHP就会启动清理程序。它会按顺序调用各个模块的RSHUTDOWN方法。RSHUTDOWN用以清除程序运行时产生的符号表,也就是对每个变量调用unset函数。 一个典型的RSHUTDOWN方法如下:PHP_RSHUTDOWN_FUNCTION(extension_name) {/ Do memory management, unset all variables used in the last PHP call etc /}PHP关闭第二步 最后,所有的请求都已处理完毕,SAPI也准备关闭了,PHP开始执行第二步:PHP调用每个扩展的MSHUTDOWN方法,这是各个模块最后一次释放内存的机会。 一个典型的RSHUTDOWN方法如下:PHP_MSHUTDOWN_FUNCTION(extension_name) {/ Free handlers and persistent memory etc /} 这样,整个PHP生命周期就结束了。要注意的是,只有在服务器没有请求的情况下才会执行“启动第一步”和“关闭第二步”。下面的是用一些图示来说明的!PHP底层工作原理wps_clip_image-29471图1 php结构从图上可以看出,php从下到上是一个4层体系①Zend引擎Zend整体用纯c实现,是php的内核部分,它将php代码翻译(词法、语法解析等一系列编译过程)为可执行opcode的处理并实现相应的处理方法、实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的api方法供外部调用,是一切的核心,所有的外围功能均围绕zend实现。②Extensions围绕着zend引擎,extensions通过组件式的方式提供各种基础服务,我们常见的各种内置函数(如array系列)、标准库等都是通过extension来实现,用户也可以根据需要实现自己的extension以达到功能扩展、性能优化等目的(如贴吧正在使用的php中间层、富文本解析就是extension的典型应用)。③SapiSapi全称是Server Application Programming Interface,也就是服务端应用编程接口,sapi通过一系列钩子函数,使得php可以和外围交互数据,这是php非常优雅和成功的一个设计,通过sapi成功的将php本身和上层应用解耦隔离,php可以不再考虑如何针对不同应用进行兼容,而应用本身也可以针对自己的特点实现不同的处理方式。后面将在sapi章节中介绍④上层应用这就是我们平时编写的php程序,通过不同的sapi方式得到各种各样的应用模式,如通过webserver实现web应用、在命令行下以脚本方式运行等等。构架思想:引擎(Zend)+组件(ext)的模式降低内部耦合中间层(sapi)隔绝web server和php如果php是一辆车,那么车的框架就是php本身Zend是车的引擎(发动机)Ext下面的各种组件就是车的轮子Sapi可以看做是公路,车可以跑在不同类型的公路上而一次php程序的执行就是汽车跑在公路上。因此,我们需要:性能优异的引擎+合适的车轮+正确的跑道Apache和php的关系Apache对于php的解析,就是通过众多Module中的php Module来完成的。wps_clip_image-31721把php最终集成到Apache系统中,还需要对Apache进行一些必要的设置。这里,我们就以php的mod_php5 SAPI运行模式为例进行讲解,至于SAPI这个概念后面我们还会详细讲解。假定我们安装的版本是Apache2 和 Php5,那么需要编辑Apache的主配置文件http.conf,在其中加入下面的几行内容:Unix/Linux环境下:LoadModule php5_module modules/mod_php5.soAddType application/x-httpd-php .php注:其中modules/mod_php5.so 是X系统环境下mod_php5.so文件的安装位置。Windows环境下:LoadModule php5_module d:/php/php5apache2.dllAddType application/x-httpd-php .php注:其中d:/php/php5apache2.dll 是在Windows环境下php5apache2.dll文件的安装位置。这两项配置就是告诉Apache Server,以后收到的Url用户请求,凡是以php作为后缀,就需要调用php5_module模块(mod_php5.so/ php5apache2.dll)进行处理。Apache的生命周期wps_clip_image-8490Apach的请求处理流程wps_clip_image-17917Apache请求处理循环详解Apache请求处理循环的11个阶段都做了哪些事情呢? 1、Post-Read-Request阶段在正常请求处理流程中,这是模块可以插入钩子的第一个阶段。对于那些想很早进入处理请求的模块来说,这个阶段可以被利用。 2、URI Translation阶段 Apache在本阶段的主要工作:将请求的URL映射到本地文件系统。模块可以在这阶段插入钩子,执行自己的映射逻辑。mod_alias就是利用这个阶段工作的。 3、Header Parsing阶段 Apache在本阶段的主要工作:检查请求的头部。由于模块可以在请求处理流程的任何一个点上执行检查请求头部的任务,因此这个钩子很少被使用。mod_setenvif就是利用这个阶段工作的。 4、Access Control阶段 Apache在本阶段的主要工作:根据配置文件检查是否允许访问请求的资源。Apache的标准逻辑实现了允许和拒绝指令。mod_authz_host就是利用这个阶段工作的。 5、Authentication阶段 Apache在本阶段的主要工作:按照配置文件设定的策略对用户进行认证,并设定用户名区域。模块可以在这阶段插入钩子,实现一个认证方法。 6、Authorization阶段 Apache在本阶段的主要工作:根据配置文件检查是否允许认证过的用户执行请求的操作。模块可以在这阶段插入钩子,实现一个用户权限管理的方法。 7、MIME Type Checking阶段 Apache在本阶段的主要工作:根据请求资源的MIME类型的相关规则,判定将要使用的内容处理函数。标准模块mod_negotiation和mod_mime实现了这个钩子。 8、FixUp阶段 这是一个通用的阶段,允许模块在内容生成器之前,运行任何必要的处理流程。和Post_Read_Request类似,这是一个能够捕获任何信息的钩子,也是最常使用的钩子。 9、Response阶段 Apache在本阶段的主要工作:生成返回客户端的内容,负责给客户端发送一个恰当的回复。这个阶段是整个处理流程的核心部分。 10、Logging阶段 Apache在本阶段的主要工作:在回复已经发送给客户端之后记录事务。模块可能修改或者替换Apache的标准日志记录。 11、CleanUp阶段Apache在本阶段的主要工作:清理本次请求事务处理完成之后遗留的环境,比如文件、目录的处理或者Socket的关闭等等,这是Apache一次请求处理的最后一个阶段。 LAMP架构:wps_clip_image-24435从下往上四层:①liunx 属于操作系统的底层②apache服务器,属于次服务器,沟通linux和PHP③php:属于服务端编程语言,通过php_module 模块 和apache关联④mysql和其他web服务:属于应用服务,通过PHP的Extensions外 挂模块和mysql关联 Android系统架构图lamp和安卓的架构图比较一下,貌似和lamp架构有点相似,本人不懂安卓,只是感觉上有点相似,高手可以指出区别,小弟在此不胜感谢wps_clip_image-27187从上往下:安卓架构--------------说明--------LAMP架构1.应用程序 --------具体应用--------web应用2.应用程序框架 ----java-------------PHP语言和库3.系统运行库 :----虚拟机---------WEB服务器⒋Linux 内核 :---操作系统-------lamp架构中的L
2023年08月07日
13 阅读
0 评论
0 点赞
2023-08-07
使用Composer 管理PHP代码项目
使用Composer 管理PHP代码项目简介Composer 是 PHP 的一个依赖管理工具。它允许你申明项目所依赖的代码库,它会在你的项目中为你安装他们。简介依赖管理声明依赖关系系统要求安装 - *nix下载 Composer 的可执行文件局部安装全局安装全局安装 (on OSX via homebrew)安装 - Windows使用安装程序手动安装使用 Composer自动加载依赖管理Composer 不是一个包管理器。是的,它涉及 "packages" 和 "libraries",但它在每个项目的基础上进行管理,在你项目的某个目录中(例如 vendor)进行安装。默认情况下它不会在全局安装任何东西。因此,这仅仅是一个依赖管理。这种想法并不新鲜,Composer 受到了 node's npm 和 ruby's bundler 的强烈启发。而当时 PHP 下并没有类似的工具。Composer 将这样为你解决问题:a) 你有一个项目依赖于若干个库。b) 其中一些库依赖于其他库。c) 你声明你所依赖的东西。d) Composer 会找出哪个版本的包需要安装,并安装它们(将它们下载到你的项目中)。声明依赖关系比方说,你正在创建一个项目,你需要一个库来做日志记录。你决定使用 monolog。为了将它添加到你的项目中,你所需要做的就是创建一个 composer.json 文件,其中描述了项目的依赖关系。{ "require": { "monolog/monolog": "1.2.*" } }我们只要指出我们的项目需要一些 monolog/monolog 的包,从 1.2 开始的任何版本。系统要求运行 Composer 需要 PHP 5.3.2+ 以上版本。一些敏感的 PHP 设置和编译标志也是必须的,但对于任何不兼容项安装程序都会抛出警告。我们将从包的来源直接安装,而不是简单的下载 zip 文件,你需要 git 、 svn 或者 hg ,这取决于你载入的包所使用的版本管理系统。Composer 是多平台的,我们努力使它在 Windows 、 Linux 以及 OSX 平台上运行的同样出色。安装 - *nix下载 Composer 的可执行文件局部安装要真正获取 Composer,我们需要做两件事。首先安装 Composer (同样的,这意味着它将下载到你的项目中):curl -sS https://getcomposer.org/installer | php注意: 如果上述方法由于某些原因失败了,你还可以通过 php >下载安装器:php -r "readfile('https://getcomposer.org/installer');" | php这将检查一些 PHP 的设置,然后下载 composer.phar 到你的工作目录中。这是 Composer 的二进制文件。这是一个 PHAR 包(PHP 的归档),这是 PHP 的归档格式可以帮助用户在命令行中执行一些操作。你可以通过 --install-dir 选项指定 Composer 的安装目录(它可以是一个绝对或相对路径):curl -sS https://getcomposer.org/installer | php -- --install-dir=bin全局安装你可以将此文件放在任何地方。如果你把它放在系统的 PATH 目录中,你就能在全局访问它。 在类Unix系统中,你甚至可以在使用时不加 php 前缀。你可以执行这些命令让 composer 在你的系统中进行全局调用:curl -sS https://getcomposer.org/installer | php mv composer.phar /usr/local/bin/composer注意: 如果上诉命令因为权限执行失败, 请使用 sudo 再次尝试运行 mv 那行命令。现在只需要运行 composer 命令就可以使用 Composer 而不需要输入 php composer.phar。全局安装 (on OSX via homebrew)Composer 是 homebrew-php 项目的一部分。brew update brew tap josegonzalez/homebrew-php brew tap homebrew/versions brew install php55-intl brew install josegonzalez/php/composer安装 - Windows使用安装程序这是将 Composer 安装在你机器上的最简单的方法。下载并且运行 Composer-Setup.exe,它将安装最新版本的 Composer ,并设置好系统的环境变量,因此你可以在任何目录下直接使用 composer 命令。手动安装设置系统的环境变量 PATH 并运行安装命令下载 composer.phar 文件:C:\Users\username>cd C:\bin C:\bin>php -r "readfile('https://getcomposer.org/installer');" | php注意: 如果收到 readfile 错误提示,请使用 http 链接或者在 php.ini 中开启 php_openssl.dll 。在 composer.phar 同级目录下新建文件 composer.bat :C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat关闭当前的命令行窗口,打开新的命令行窗口进行测试:C:\Users\username>composer -V Composer version 27d8904使用 Composer现在我们将使用 Composer 来安装项目的依赖。如果在当前目录下没有一个 composer.json 文件,请查看基本用法章节。要解决和下载依赖,请执行 install 命令:php composer.phar install如果你进行了全局安装,并且没有 phar 文件在当前目录,请使用下面的命令代替:composer install继续 上面的例子,这里将下载 monolog 到 vendor/monolog/monolog 目录。自动加载除了库的下载,Composer 还准备了一个自动加载文件,它可以加载 Composer 下载的库中所有的类文件。使用它,你只需要将下面这行代码添加到你项目的引导文件中:require 'vendor/autoload.php';现在我们就可以使用 monolog 了!想要学习更多关于 Composer 的知识,请查看“基本用法”章节。基本用法基本用法安装composer.json:项目安装关于 require Key包名称包版本下一个重要版本(波浪号运算符)稳定性安装依赖包composer.lock - 锁文件Packagist自动加载安装安装 Composer,你只需要下载 composer.phar 可执行文件。curl -sS https://getcomposer.org/installer | php详细请查看 简介 章节。要检查 Composer 是否正常工作,只需要通过 php 来执行 PHAR:php composer.phar这将返回给你一个可执行的命令列表。注意: 你也可以仅执行 --check 选项而无需下载 Composer。 要获取更多的信息请使用 --help。curl -sS https://getcomposer.org/installer | php -- --helpcomposer.json:项目安装要开始在你的项目中使用 Composer,你只需要一个 composer.json 文件。该文件包含了项目的依赖和其它的一些元数据。这个 JSON format 是很容易编写的。它允许你定义嵌套结构。关于 require Key第一件事情(并且往往只需要做这一件事),你需要在 composer.json 文件中指定 require key 的值。你只需要简单的告诉 Composer 你的项目需要依赖哪些包。{ "require": { "monolog/monolog": "1.0.*" } }你可以看到, require 需要一个 包名称 (例如 monolog/monolog) 映射到 包版本 (例如 1.0.*) 的对象。包名称包名称由供应商名称和其项目名称构成。通常容易产生相同的项目名称,而供应商名称的存在则很好的解决了命名冲突的问题。它允许两个不同的人创建同样名为 json 的库,而之后它们将被命名为 igorw/json 和 seldaek/json。这里我们需要引入 monolog/monolog,供应商名称与项目的名称相同,对于一个具有唯一名称的项目,我们推荐这么做。它还允许以后在同一个命名空间添加更多的相关项目。如果你维护着一个库,这将使你可以很容易的把它分离成更小的部分。包版本在前面的例子中,我们引入的 monolog 版本指定为 1.0.*。这表示任何从 1.0 开始的开发分支,它将会匹配 1.0.0、1.0.2 或者 1.0.20。版本约束可以用几个不同的方法来指定。名称 实例 描述确切的版本号 1.0.2 你可以指定包的确切版本。范围 >=1.0 >=1.0,<2.0>=1.0,<1.1|>=1.2 通过使用比较操作符可以指定有效的版本范围。有效的运算符:>、>=、<、<=、!=。你可以定义多个范围,用逗号隔开,这将被视为一个逻辑AND处理。一个管道符号|将作为逻辑OR处理。AND 的优先级高于 OR。通配符 1.0. 你可以使用通配符来指定一种模式。1.0.*与>=1.0,<1.1是等效的。赋值运算符 ~1.2 这对于遵循语义化版本号的项目非常有用。~1.2相当于>=1.2,<2.0。想要了解更多,请阅读下一小节。下一个重要版本(波浪号运算符)~ 最好用例子来解释: ~1.2 相当于 >=1.2,<2.0,而 ~1.2.3 相当于 >=1.2.3,<1.3。正如你所看到的这对于遵循 语义化版本号 的项目最有用。一个常见的用法是标记你所依赖的最低版本,像 ~1.2 (允许1.2以上的任何版本,但不包括2.0)。由于理论上直到2.0应该都没有向后兼容性问题,所以效果很好。你还会看到它的另一种用法,使用 ~ 指定最低版本,但允许版本号的最后一位数字上升。注意: 虽然 2.0-beta.1 严格地说是早于 2.0,但是,根据版本约束条件, 例如 ~1.2 却不会安装这个版本。就像前面所讲的 ~1.2 只意味着 .2 部分可以改变,但是 1. 部分是固定的。稳定性默认情况下只有稳定的发行版才会被考虑在内。如果你也想获得 RC、beta、alpha 或 dev 版本,你可以使用 稳定标志。你可以对所有的包做 最小稳定性 设置,而不是每个依赖逐一设置。安装依赖包获取定义的依赖到你的本地项目,只需要调用 composer.phar 运行 install 命令。php composer.phar install接着前面的例子,这将会找到 monolog/monolog 的最新版本,并将它下载到 vendor 目录。 这是一个惯例把第三方的代码到一个指定的目录 vendor。如果是 monolog 将会创建 vendor/monolog/monolog 目录。小技巧: 如果你正在使用Git来管理你的项目, 你可能要添加 vendor 到你的 .gitignore 文件中。 你不会希望将所有的代码都添加到你的版本库中。另一件事是 install 命令将创建一个 composer.lock 文件到你项目的根目录中。composer.lock - 锁文件在安装依赖后,Composer 将把安装时确切的版本号列表写入 composer.lock 文件。这将锁定改项目的特定版本。请提交你应用程序的 composer.lock (包括 composer.json)到你的版本库中这是非常重要的,因为 install 命令将会检查锁文件是否存在,如果存在,它将下载指定的版本(忽略 composer.json 文件中的定义)。这意味着,任何人建立项目都将下载与指定版本完全相同的依赖。你的持续集成服务器、生产环境、你团队中的其他开发人员、每件事、每个人都使用相同的依赖,从而减轻潜在的错误对部署的影响。即使你独自开发项目,在六个月内重新安装项目时,你也可以放心的继续工作,即使从那时起你的依赖已经发布了许多新的版本。如果不存在 composer.lock 文件,Composer 将读取 composer.json 并创建锁文件。这意味着如果你的依赖更新了新的版本,你将不会获得任何更新。此时要更新你的依赖版本请使用 update 命令。这将获取最新匹配的版本(根据你的 composer.json 文件)并将新版本更新进锁文件。php composer.phar update如果只想安装或更新一个依赖,你可以白名单它们:php composer.phar update monolog/monolog [...]注意: 对于库,并不一定建议提交锁文件 请参考:库的锁文件.Packagistpackagist 是 Composer 的主要资源库。 一个 Composer 的库基本上是一个包的源:记录了可以得到包的地方。Packagist 的目标是成为大家使用库资源的中央存储平台。这意味着你可以 require 那里的任何包。当你访问 packagist website (packagist.org),你可以浏览和搜索资源包。任何支持 Composer 的开源项目应该发布自己的包在 packagist 上。虽然并不一定要发布在 packagist 上来使用 Composer,但它使我们的编程生活更加轻松。自动加载对于库的自动加载信息,Composer 生成了一个 vendor/autoload.php 文件。你可以简单的引入这个文件,你会得到一个免费的自动加载支持。require 'vendor/autoload.php';这使得你可以很容易的使用第三方代码。例如:如果你的项目依赖 monolog,你就可以像这样开始使用这个类库,并且他们将被自动加载。$log = new Monolog\Logger('name'); $log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING)); $log->addWarning('Foo');你可以在 composer.json 的 autoload 字段中增加自己的 autoloader。{ "autoload": { "psr-4": {"Acme\": "src/"} } }Composer 将注册一个 PSR-4 autoloader 到 Acme 命名空间。你可以定义一个从命名空间到目录的映射。此时 src 会在你项目的根目录,与 vendor 文件夹同级。例如 src/Foo.php 文件应该包含 Acme\Foo 类。添加 autoload 字段后,你应该再次运行 install 命令来生成 vendor/autoload.php 文件。引用这个文件也将返回 autoloader 的实例,你可以将包含调用的返回值存储在变量中,并添加更多的命名空间。这对于在一个测试套件中自动加载类文件是非常有用的,例如。$loader = require 'vendor/autoload.php'; $loader->add('Acme\Test\', __DIR__);除了 PSR-4 自动加载,classmap 也是支持的。这允许类被自动加载,即使不符合 PSR-0 规范。详细请查看 自动加载-参考。注意: Composer 提供了自己的 autoloader。如果你不想使用它,你可以仅仅引入 vendor/composer/autoload_*.php 文件,它返回一个关联数组,你可以通过这个关联数组配置自己的 autoloader。库(资源包)本章将告诉你如何通过 Composer 来安装你的库。库(资源包)每一个项目都是一个包平台软件包指明版本标签分支别名锁文件发布到 VCS(线上版本控制系统)发布到 packagist每一个项目都是一个包只要你有一个 composer.json 文件在目录中,那么整个目录就是一个包。当你添加一个 require 到项目中,你就是在创建一个依赖于其它库的包。你的项目和库之间唯一的区别是,你的项目是一个没有名字的包。为了使它成为一个可安装的包,你需要给它一个名称。你可以通过 composer.json 中的 name 来定义:{ "name": "acme/hello-world", "require": { "monolog/monolog": "1.0.*" } }在这种情况下项目的名称为 acme/hello-world,其中 acme 是供应商的名称。供应商的名称是必须填写的。注意: 如果你不知道拿什么作为供应商的名称, 那么使用你 github 上的用户名通常是不错的选择。 虽然包名不区分大小写,但惯例是使用小写字母,并用连字符作为单词的分隔。平台软件包Composer 将那些已经安装在系统上,但并不是由 Composer 安装的包视为一个虚拟的平台软件包。这包括PHP本身,PHP扩展和一些系统库。php 表示用户的 PHP 版本要求,你可以对其做出限制。例如 >=5.4.0。如果需要64位版本的 PHP,你可以使用 php-64bit 进行限制。hhvm 代表的是 HHVM(也就是 HipHop Virtual Machine) 运行环境的版本,并且允许你设置一个版本限制,例如,'>=2.3.3'。ext- 可以帮你指定需要的 PHP 扩展(包括核心扩展)。通常 PHP 拓展的版本可以是不一致的,将它们的版本约束为 * 是一个不错的主意。一个 PHP 扩展包的例子:包名可以写成 ext-gd。lib- 允许对 PHP 库的版本进行限制。以下是可供使用的名称:curl、iconv、icu、libxml、openssl、pcre、uuid、xsl。你可以使用 composer show --platform 命令来获取可用的平台软件包的列表。指明版本你需要一些方法来指明自己开发的包的版本,当你在 Packagist 上发布自己的包,它能够从 VCS (git, svn, hg) 的信息推断出包的版本,因此你不必手动指明版本号,并且也不建议这样做。请查看 标签 和 分支 来了解版本号是如何被提取的。如果你想要手动创建并且真的要明确指定它,你只需要添加一个 version 字段:{ "version": "1.0.0" }注意: 你应该尽量避免手动设置版本号,因为标签的值必须与标签名相匹配。标签对于每一个看起来像版本号的标签,都会相应的创建一个包的版本。它应该符合 'X.Y.Z' 或者 'vX.Y.Z' 的形式,-patch、-alpha、-beta 或 -RC 这些后缀是可选的。在后缀之后也可以再跟上一个数字。下面是有效的标签名称的几个例子:1.0.0v1.0.01.10.5-RC1v4.4.4beta2v2.0.0-alphav2.0.4-p1注意: 即使你的标签带有前缀 v, 由于在需要 require 一个版本的约束时是不允许这种前缀的, 因此 v 将被省略(例如标签 V1.0.0 将创建 1.0.0 版本)。分支对于每一个分支,都会相应的创建一个包的开发版本。如果分支名看起来像一个版本号,那么将创建一个如同 {分支名}-dev 的包版本号。例如一个分支 2.0 将产生一个 2.0.x-dev 包版本(加入了 .x 是出于技术的原因,以确保它被识别为一个分支,而 2.0.x 的分支名称也是允许的,它同样会被转换为 2.0.x-dev)。如果分支名看起来不像一个版本号,它将会创建 dev-{分支名} 形式的版本号。例如 master 将产生一个 dev-master的版本号。下面是版本分支名称的一些示例:1.x1.0 (equals 1.0.x)1.1.x注意: 当你安装一个新的版本时,将会自动从它 source 中拉取。 详细请查看 install 命令。别名它表示一个包版本的别名。例如,你可以为 dev-master 设置别名 1.0.x-dev,这样就可以通过 require 1.0.x-dev 来得到 dev-master 版本的包。详细请查看“别名”。锁文件如果你愿意,可以在你的项目中提交 composer.lock 文件。他将帮助你的团队始终针对同一个依赖版本进行测试。任何时候,这个锁文件都只对于你的项目产生影响。如果你不想提交锁文件,并且你正在使用 Git,那么请将它添加到 .gitignore 文件中。发布到 VCS(线上版本控制系统)一旦你有一个包含 composer.json 文件的库存储在线上版本控制系统(例如:Git),你的库就可以被 Composer 所安装。在这个例子中,我们将 acme/hello-world 库发布在 GitHub 上的 github.com/username/hello-world 中。现在测试这个 acme/hello-world 包,我们在本地创建一个新的项目。我们将它命名为 acme/blog。此博客将依赖 acme/hello-world,而后者又依赖 monolog/monolog。我们可以在某处创建一个新的 blog 文件夹来完成它,并且需要包含 composer.json 文件:{ "name": "acme/blog", "require": { "acme/hello-world": "dev-master" } }在这个例子中 name 不是必须的,因为我们并不想将它发布为一个库。在这里为 composer.json 文件添加描述。现在我们需要告诉我们的应用,在哪里可以找到 hello-world 的依赖。为此我们需要在 composer.json 中添加 repositories 来源申明:{ "name": "acme/blog", "repositories": [ { "type": "vcs", "url": "https://github.com/username/hello-world" } ], "require": { "acme/hello-world": "dev-master" } }更多关于包的来源是如何工作的,以及还有什么其他的类型可供选择,请查看资源库。这就是全部了。你现在可以使用 Composer 的 install 命令来安装你的依赖包了!小结: 任何含有 composer.json 的 GIT、SVN、HG 存储库,都可以通过 require 字段指定“包来源”和“声明依赖”来添加到你的项目中。发布到 packagist好的,你现在可以发布你的包了,但你不会希望你的用户每次都这样繁琐的指定包的来源。你可能注意到了另一件事,我们并没有指定 monolog/monolog 的来源。它是怎么工作的?答案是 packagist。Packagist 是 Composer 主要的一个包信息存储库,它默认是启用的。任何在 packagist 上发布的包都可以直接被 Composer 使用。就像 monolog 它被 发布在 packagist 上,我们可以直接使用它,而不必指定任何额外的来源信息。如果我们想与世界分享我们的 hello-world,我们最好将它发布到 packagist 上。这样做是很容易的。你只需要点击那个大大的 "Submit Package" 按钮并注册。接着提交你库的来源地址,此时 packagist 就开始了抓取。一旦完成,你的包将可以提供给任何人使用。
2023年08月07日
11 阅读
0 评论
0 点赞
1
...
89
90
91
...
112