从 PHP 5.4.0 起,PHP内置了Web服务器,这对于认为需要Apache或Nginx才能预览PHP应用的开发者来说又是一个隐藏功能。这个内置的Web服务器不应该用于生产环境,但对于本地开发来说是个极好的工具。Laravel Valet 起初就是使用这个内置的服务器,但是在1.1.0版本后将其替换为Caddy(查看相关新闻)。 1、启动 这个内置的Web服务器很容易启动,打开终端(Windows下对应是cmd命令行),进入项目根目录,执行如下命令即可:
1 |
php -S localhost:8000 |
上述命令会新启动一个PHP Web服务器,地址是localhost,监听的端口是8000,当前所在目录就是这个Web服务器的根目录。 现在,打开浏览器,访问http://localhost:8000就可以预览应用了。在Web浏览器中浏览应用时,每个HTTP请求的信息都会记录到终端的标准输出中,因此我们可以查看应用是否抛出了404或500响应: 有时候我们需要在同一局域网中的另一台设备中访问这个服务器(例如iPad或本地虚拟机),为此,我们可以把localhost换成0.0.0.0,让PHP Web服务器监听所有接口:
1 |
php -S 0.0.0.0:8000 |
要想停止Web服务器,可以关闭终端,也可以按Ctrl+C快捷键。 2、配置 应用常常需要使用专属的PHP配置文件,尤其是对内存使用、文件上传、分析或对字节码缓存有特殊要求时,一定要单独配置,我们可以使用-c选项,让PHP内置的服务器使用指定的配置文件:
1 |
php -S localhost:8000 -c app/config/php.ini |
3、路由脚本 PHP内置服务器明显遗漏了一个功能:与Apache和Nginx不同,它不支持.htaccess文件,因此,这个服务器很难使用多数流行的PHP框架中常见的前端控制器(单一入口文件index.php,用于转发所有HTTP请求,现在主流PHP框架如Laravel、Symfony都是这样)。 PHP内置服务器使用路由脚本弥补了这一缺憾,处理每个HTTP请求前,会先执行这个路由脚本,如果结果为false,返回当前HTTP请求中引用的静态资源URI,否则会把路由脚本的执行结果当做HTTP响应主体返回。换句话说,路由脚本的作用其实和.htaccess一样。 路由脚本的用法很简单,只需要在启动PHP内置服务器时指定这个PHP脚本文件的路径即可:
1 |
php -S localhost:8000 router.php |
关于路由脚本,有兴趣的同学可以研究下Laravel Valet底层的server.php(https://github.com/laravel/valet/blob/master/server.php)。 4、判断函数 有时候需要知道PHP脚本使用的是PHP内置的Web服务器还是使用传统的Web服务器,这样方便我们为不同服务器设定不同的响应头。我们可以使用php_sapi_name()函数检查使用的是哪个PHP Web服务器,如果当前脚本使用的是PHP内置服务器,则该函数返回字符串cli-server:
1 2 3 4 5 6 |
<?php if (php_sapi_name() == ‘cli-server') { // PHP 内置 Web 服务器 } else { // 其他Web服务器 } |
5、缺点 PHP内置的Web服务器不能在生成环境使用,只能在本地开发环境中使用,这是因为其相比Apache或Nginx有诸多不足: 性能不佳。一次只能处理一个请求,其他请求会受到阻塞。如果某个进程耗时较长(数据库查询、远程API调用),则整个Web应用会陷入停顿状态。 支持媒体类型较少(这一点PHP 5.5.7以后有较大改进)。 路由脚本仅支持少量的URL重写,更高级则还是需要Apache或Nginx。 from:http://laravelacademy.org/post/4422.html
View Details1、概述 字节码缓存不是PHP的新特性,有很多独立的扩展可以实现,比如APC、eAccelerator和Xache等,但是截至目前这些扩展都没有集成到PHP内核,从PHP 5.5.0开始,PHP内置了字节码缓存功能,名为Zend Opcache。 开始之前,我们先来看看什么是字节码缓存,以及字节码缓存的作用是什么。 众所周知,PHP是解释型语言,构建在Zend 虚拟机之上,PHP解释器在执行PHP脚本时会解析PHP脚本代码,把PHP代码编译成一系列Zend操作码(opcode:http://php.net/manual/zh/internals2.opcodes.php,由于每个操作码都是一个字节长,所以又叫字节码,字节码可以直接被Zend虚拟机执行),然后执行字节码。每次请求PHP文件都是这样,这会消耗很多资源,如果每次HTTP请求都必须不断解析、编译和执行PHP脚本,消耗的资源更多。如果PHP源码不变,相应的字节码也不会变化,显然没有必要每次都重新生成Opcode,结合在Web应用中无处不在的缓存机制,我们可以把首次生成的Opcode缓存起来,这样下次直接从缓存取,岂不是很快?下面是启用Opcode缓存之前和之后的流程图: 字节码缓存能存储预先编译好的PHP字节码,这样,下次请求PHP脚本时,PHP解释器不用每次读取、解析和编译PHP代码,直接从内存中读取预先编译好的字节码,然后立即执行,这样能省很多时间,极大提升应用的性能。 2、启用Zend Opcache 注:如果使用Windows开发环境,或者使用brew或apt-get等命令安装的PHP可以略过编译步骤。 默认情况下,Zend Opcache没有开启,需要我们在编译时使用--enable-opcache指定启用Zend Opcache。 编译好PHP后还需要在php.ini中指定Opcache扩展路径:
1 |
zend_extension=/path/to/opcache.so |
一般而言PHP编译成功后会显示Zend Opcache扩展路径,但如果想不起来,可以使用如下命令找到PHP扩展所在目录:
1 |
php-config --extension-dir |
注:如果你使用Xdebug,需要在php.ini中先加载Zend Opcache,再加载Xdebug。 更新php.ini后重启PHP进程并查看是否启用成功: 3、配置Zend Opcache 启用Zend Opcache后还需要在php.ini中配置Zend Opcache,下面是一份配置示例作为参考:
1 2 3 4 5 6 |
opcache.validate_timestamps=1 //生产环境中配置为0 opcache.revalidate_freq=0 //检查脚本时间戳是否有更新时间 opcache.memory_consumption=64 //Opcache的共享内存大小,以M为单位 opcache.interned_strings_buffer=16 //用来存储临时字符串的内存大小,以M为单位 opcache.max_accelerated_files=4000 //Opcache哈希表可以存储的脚本文件数量上限 opcache.fast_shutdown=1 //使用快速停止续发事件 |
注:后续我们还会进一步介绍Zend Opcache的配置,PHP官网中列出了Zend Opcache的全部设置:http://ua2.php.net/manual/zh/opcache.configuration.php。 4、使用Zend Opcache Zend Opcache使用起来很简单,因为启用之后它会自动运行,Zend Opcache会自动在内存中缓存预先编译好的PHP字节码,如果缓存了某个文件的字节码,就执行对应的字节码。 如果php.ini中配置了opcache.validate_timestamps值为0,需要小心,因为Zend Opcache将不能觉察PHP脚本的变化,必须手动清空Zend OPcache缓存的字节码,才能让它发现PHP文件的变动。这个配置适合在生产环境中设置为0,但在开发环境会带来不便,我们可以在开发环境中这样配置启用自动验证缓存功能:
1 2 3 4 |
opcache.validate_timestamps=1 opcache.revalidate_freq=0 from:<a href="http://laravelacademy.org/post/4396.html">http://laravelacademy.org/post/4396.html</a> |
1、概述 闭包和匿名函数在PHP 5.3.0中引入,这两个特性非常有用,每个PHP开发者都应该掌握。 闭包是指在创建时封装周围状态的函数,即使闭包所在的环境的不存在了,闭包中封装的状态依然存在。 匿名函数其实就是没有名称的函数,匿名函数可以赋值给变量,还能像其他任何PHP函数对象那样传递。不过匿名函数仍然是函数,因此可以调用,还可以传入参数,适合作为函数或方法的回调。 注:理论上讲闭包和匿名函数是不同的概念,不过PHP将其视作相同的概念(匿名函数在PHP中也叫作闭包函数),所以下面提到闭包时指的也是匿名函数;反之亦然。 2、创建闭包 创建闭包很简单:
1 2 3 4 5 6 |
<?php $greet = function ($name) { return sprintf("Hello %s\r\n", $name); }; echo $greet('LaravelAcademy.org'); |
结果打印:
1 |
Hello LaravelAcademy.org |
闭包和普通的PHP函数很像:常用的句法相同,也接受参数,而且能返回值。不过闭包没有函数名。 注:我们之所以能调用$greet变量,是因为这个变量的值是一个闭包,而且闭包对象实现了__invoke()魔术方法,只要变量名后有(),PHP就会查找并调用__invoke方法。 我们通常把PHP闭包当做函数会方法的回调使用,事实上,很多PHP函数都会用到闭包,比如array_map和preg_replace_callback,这是使用PHP匿名函数的绝佳时机。记住,闭包和其他值一样,可以作为参数传入其他PHP函数:
1 2 3 4 5 6 |
<?php $numberPlusOne = array_map(function ($number) { return $number += 1; }, [1, 2, 3]); print_r($numberPlusOne); |
在闭包出现之前,要实现这样的功能,PHP开发者只能单独创建具名函数,然后使用名称引用这个函数:
1 2 3 4 5 6 7 |
<?php function incrementNumber ($number) { return $number += 1; } $numberPlusOne = array_map(‘incrementNumber’, [1, 2, 3]); print_r($numberPlusOne); |
这样做把回调的实现和使用场所隔离开了,而且使用闭包实现代码更加简洁。 3、从父作用域继承变量 在PHP中必须手动调用闭包对象的bindTo方法或使用use关键字把父作用域的变量及状态附加到PHP闭包中。而实际应用中,又以使用use关键字实现居多。 use关键字 实际上,Laravel框架中也大量使用了闭包,最常见的比如路由定义:
1 2 3 4 5 |
Route::group(['domain' => '{account}.myapp.com'], function () { Route::get('user/{id}', function ($account, $id) { // }); }); |
这里面的两个function都是闭包。而从父作用域继承变量的使用场景在Laravel底层源码中也是俯拾即是,比如Model.php(Illuminate\Database\Eloquent)的saveOrFail方法: 该方法的作用是使用事务将模型数据保存到数据库,这里面我们使用闭包返回保存状态,同时使用use关键字将父作用域的$options传递给该闭包以便其能够访问这个数据。 此外,还支持传递多个父作用域变量到闭包,比如还是在Model类中的forceFill方法: 多个变量以逗号分隔即可。 bindTo方法 我们在前面已经提到,闭包是一个对象,所以我们可以在闭包中使用$this关键字获取闭包的内部状态,闭包对象的默认状态没什么用,需要注意的是其中的__invoke魔术方法和bindTo方法。 __invoke的作用前面已经说过,当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。 接下来我们来看看bindTo方法,通过该方法,我们可以把闭包的内部状态绑定到其他对象上。这里bindTo方法的第二个参数显得尤为重要,其作用是指定绑定闭包的那个对象所属的PHP类,这样,闭包就可以在其他地方访问邦定闭包的对象中受保护和私有的成员变量。 你会发现,PHP框架经常使用bindTo方法把路由URL映射到匿名回调函数上,框架会把匿名回调函数绑定到应用对象上,这样在匿名函数中就可以使用$this关键字引用重要的应用对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php class App { protected $routes = []; protected $responseStatus = '200 OK'; protected $responseContentType = 'text/html'; protected $responseBody = 'Laravel学院'; public function addRoute($routePath, $routeCallback) { $this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__); } public function dispatch($currentPath) { foreach ($this->routes as $routePath => $callback) { if( $routePath === $currentPath) { $callback(); } } header('HTTP/1.1 ' . $this->responseStatus); header('Content-Type: ' . $this->responseContentType); header('Content-Length: ' . mb_strlen($this->responseBody)); echo $this->responseBody; } } |
这里我们需要重点关注addRoute方法,这个方法的参数分别是一个路由路径和一个路由回调,dispatch方法的参数是当前HTTP请求的路径,它会调用匹配的路由回调。第9行是重点所在,我们将路由回调绑定到了当前的App实例上。这么做能够在回调函数中处理App实例的状态:
1 2 3 4 5 6 |
$app = new App(); $app->addRoute(‘user/nonfu’, function(){ $this->responseContentType = ‘application/json;charset=utf8’; $this->responseBody = ‘{“name”:”LaravelAcademy"}'; }); $app->dispatch(‘user/nonfu'); |
在Larval底层也有用到bindTo方法,详见Illuminate\Support\Traits\Macroable的__call方法: from:http://laravelacademy.org/post/4341.html
View Details1、概述 生成器是 PHP 5.5 引入的新特性,但是目测很少人用到它,其实这是个非常有用的功能。 生成器和迭代器有点类似,但是与标准的PHP迭代器不同,PHP生成器不要求类实现Iterator接口,从而减轻了类的开销和负担。生成器会根据需求每次计算并产出需要迭代的值,这对应用的性能有很大的影响:试想假如标准的PHP迭代器经常在内存中执行迭代操作,这要预先计算出数据集,性能低下;如果要使用特定方式计算大量数据,如操作Excel表数据,对性能影响更甚。此时我们可以使用生成器,即时计算并产出后续值,不占用宝贵的内存空间。 2、创建生成器 生成器的创建方式很简单,因为生成器就是PHP函数,只不过要在函数中一次或多次使用yield关键字。与普通的PHP函数不同的是,生成器从不返回值,只产出值。下面是一个简单的生成器实现:
1 2 3 4 5 |
function getLaravelAcademy() { yield 'http://LaravelAcademy.org'; yield 'Laravel学院'; yield 'Laravel Academy'; } |
很简单吧!调用此生成器函数时,PHP会返回一个属于Generator类的对象,这个对象可以使用foreach函数迭代,每次迭代,PHP会要求Generator实例计算并提供下一个要迭代的值。生成器的优雅体现在每次产出一个值之后,生成器的内部状态都会停顿;向生成器请求下一个值时,内部状态又会恢复。生成器内部的状态会一直在停顿和恢复之间切换,直到抵达函数定义体的末尾或遇到空的return语句为止。我们可以使用下面的代码调用并迭代上面定义的生成器:
1 2 3 |
foreach(getLaravelAcademy() as $yieldedValue) { echo $yieldedValue, PHP_EOL; } |
上面代码输出如下:
1 2 3 |
http://LaravelAcademy.org Laravel学院 Laravel Academy |
3、使用生成器 下面我们实现一个简单的函数用于生成一个范围内的数值,以此说明生成器是如何节省内存的。首先我们通过迭代器来实现:
1 2 3 4 5 6 7 8 9 10 11 12 |
function makeRange($length) { $dataSet = []; for ($i=0; $i<$length; $i++) { $dataSet[] = $i; } return $dataSet; } $customRange = makeRange(1000000); foreach ($customRange as $i) { echo $i . PHP_EOL; } |
此时执行会报错,提示超出单个PHP进程内存限制(要为100万个数字提供内存空间): 下面我们来改进实现方案,使用生成器实现如下:
1 2 3 4 5 6 7 8 9 |
function makeRange($length) { for ($i=0; $i<$length; $i++) { yield $i; } } foreach (makeRange(1000000) as $i) { echo $i . PHP_EOL; } |
再次执行就可以毫无压力的打印出结果,因为生成器每次只需要为一个整数分配内存。 此外,一个常用的使用案例就是使用生成器迭代流资源(文件、音频等)。假设我们想要迭代一个大小为4GB的CSV文件,而虚拟私有服务器(VPS)只允许PHP使用1GB内存,因此不能把整个文件都加载到内存中,下面的代码展示了如何使用生成器完成这种操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function getRows($file) { $handle = fopen($file, 'rb'); if ($handle == FALSE) { throw new Exception(); } while (feof($handle) === FALSE) { yield fgetcsv($handle); } fclose($handle); } foreach ($getRows($file) as $row) { print_r($row); } |
上述示例一次只会为CSV文件中的一行分配内存,而不会把整个4GB的CSV文件都读取到内存中。 4、总结 生成器是功能多样性和简洁性之间的折中方案,生成器只是向前进的迭代器,这意味着不能使用生成器在数据集中执行后退、快进或查找操作,只能让生成器计算并产出下一个值。迭代大型数据集或数列时最适合使用生成器,因为这样占用的系统内存最少。生成器也能完成迭代器能完成的简单任务,而且使用的代码更少。 总而言之,生成器并没有为PHP添加新功能,不过使用生成器大大简化了某些任务,而且使用的内存更少,如果需要更多功能,例如在数据集中执行后退、快进以及查找功能,最好自己编写实现Iterator接口的类,或者使用PHP标准库(SPL)中某个原生的迭代器(http://php.net/manual/spl.iterators.php)。 from:http://laravelacademy.org/post/4317.html
View DetailsTrait是PHP 5.4引入的新概念,看上去既像类又像接口,其实都不是,Trait可以看做类的部分实现,可以混入一个或多个现有的PHP类中,其作用有两个:表明类可以做什么;提供模块化实现。Trait是一种代码复用技术,为PHP的单继承限制提供了一套灵活的代码复用机制。 为什么使用Trait PHP语言使用一种典型的单继承模型,在这种模型中,我们先编写一个通用的根类,实现基本的功能,然后扩展这个根类,创建更具体的子类,直接从父类继承实现。这叫做继承层次结构,很多编程语言都使用这个模式。大多数时候这种典型的继承模型能够良好运作,但是如果想让两个无关的PHP类具有类似的行为,应该怎么做呢? Trait就是为了解决这种问题而诞生的。Trait能够把模块化的实现方式注入多个无关的类中,从而提高代码复用,符合DRY(Don’t Repeat Yourself)原则。比如Laravel底层用户认证相关逻辑以及软删除实现等地方都使用了Trait来实现。以Laravel自带的AuthController为例,其中的登录、注册以及登录失败尝试次数都是通过Trait实现: 如何创建Trait 创建Trait很简单,跟创建类有点类似,只不过使用的关键字是trait而不是class,以上述ThrottlesLogin为例: 我们通过trait声明定义的是一个Trait,然后我们可以在这个Trait中像类一样定义要使用的属性和方法。 此外Trait支持嵌套和组合,即通过一个或多个Trait(多个用,分隔)组合成一个Trait,比如AuthenticatesAndRegistersUsers即是如此: 使用多个Trait可能会引起命名冲突问题,上面的代码给出了解决方案:使用insteadof关键字,如果AuthenticatesUsers和RegistersUsers中都定义了redirectPath和getGuard方法,那么将从AuthenticatesUsers中获取对应方法而不是RegistersUsers。另外还可以使用as关键字为方法起个别名,这样也可以避免命名冲突。 此外,这里可能没有完整列出,Trait中还支持定义抽象方法和静态方法,其中抽象方法必须在使用它的类中实现。 这里还需要声明的一点是调用方法的优先级:调用类>Trait>父类(如果有的话),方法可以覆盖,但属性不行,如果Trait中定义了一个属性,如果调用类中也定义这个属性则会报错。 如何使用Trait Trait的使用方法也很简单,上面已经显示的很清楚明了,即使用use关键字。 可能你已经注意到,命名空间和Trait使用的都是use关键字,不同之处在于导入位置,命名空间在类的定义体外导入,而Trait在类的定义体内导入。 from:http://laravelacademy.org/post/4281.html
View Details接口不是现代PHP的新特性,但是非常重要,学会使用接口,可以极大提升我们的编程能力,所以在日常开发中应该尽可能多地使用接口。 接口是两个PHP对象之间的契约(Contract),Laravel底层就直接将接口放在Contracts目录中: 接口将我们的代码和依赖解耦了,而且允许我们的代码依赖任何实现了预期接口的第三方代码,我们不管第三方代码是如何实现接口的,只关心第三方代码是否实现了指定的接口。 如果我们编写的代码需要处理指定类的对象,那么代码的功用就完全被限定了,因为始终只能使用那个类的对象,可是,如果编写的代码处理的是接口,那么代码立即就能知道如何处理实现这一接口的任何对象,我们不关心接口是如何实现的,只关心是否实现了指定的接口。 我们以上述Laravel底层提供的CacheStore(Store接口)为例,这个接口的作用是封装缓存存储器的通用方法,包括get、put、flush等:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
<?php namespace Illuminate\Contracts\Cache; interface Store { /** * Retrieve an item from the cache by key. * * @param string|array $key * @return mixed */ public function get($key); /** * Retrieve multiple items from the cache by key. * * Items not found in the cache will have a null value. * * @param array $keys * @return array */ public function many(array $keys); /** * Store an item in the cache for a given number of minutes. * * @param string $key * @param mixed $value * @param int $minutes * @return void */ public function put($key, $value, $minutes); /** * Store multiple items in the cache for a given number of minutes. * * @param array $values * @param int $minutes * @return void */ public function putMany(array $values, $minutes); /** * Increment the value of an item in the cache. * * @param string $key * @param mixed $value * @return int|bool */ public function increment($key, $value = 1); /** * Decrement the value of an item in the cache. * * @param string $key * @param mixed $value * @return int|bool */ public function decrement($key, $value = 1); /** * Store an item in the cache indefinitely. * * @param string $key * @param mixed $value * @return void */ public function forever($key, $value); /** * Remove an item from the cache. * * @param string $key * @return bool */ public function forget($key); /** * Remove all items from the cache. * * @return void */ public function flush(); /** * Get the cache key prefix. * * @return string */ public function getPrefix(); } |
这么做的好处是可以分开定义具体的缓存实现方式,比如Laravel支持数组(Array)、数据库(Database)、文件(File)、Apc、Memcache、Redis等缓存存储器,方便我们在代码中使用相应的方式对数据进行缓存。我们以Memcached驱动为例,其对应实现类是MemcachedStore:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
<?php namespace Illuminate\Cache; use Memcached; use Illuminate\Contracts\Cache\Store; class MemcachedStore extends TaggableStore implements Store { /** * The Memcached instance. * * @var \Memcached */ protected $memcached; /** * A string that should be prepended to keys. * * @var string */ protected $prefix; /** * Create a new Memcached store. * * @param \Memcached $memcached * @param string $prefix * @return void */ public function __construct($memcached, $prefix = '') { $this->setPrefix($prefix); $this->memcached = $memcached; } /** * Retrieve an item from the cache by key. * * @param string|array $key * @return mixed */ public function get($key) { $value = $this->memcached->get($this->prefix.$key); if ($this->memcached->getResultCode() == 0) { return $value; } } /** * Retrieve multiple items from the cache by key. * * Items not found in the cache will have a null value. * * @param array $keys * @return array */ public function many(array $keys) { $prefixedKeys = array_map(function ($key) { return $this->prefix.$key; }, $keys); $values = $this->memcached->getMulti($prefixedKeys, null, Memcached::GET_PRESERVE_ORDER); if ($this->memcached->getResultCode() != 0) { return array_fill_keys($keys, null); } return array_combine($keys, $values); } /** * Store an item in the cache for a given number of minutes. * * @param string $key * @param mixed $value * @param int $minutes * @return void */ public function put($key, $value, $minutes) { $this->memcached->set($this->prefix.$key, $value, $minutes * 60); } /** * Store multiple items in the cache for a given number of minutes. * * @param array $values * @param int $minutes * @return void */ public function putMany(array $values, $minutes) { $prefixedValues = []; foreach ($values as $key => $value) { $prefixedValues[$this->prefix.$key] = $value; } $this->memcached->setMulti($prefixedValues, $minutes * 60); } /** * Store an item in the cache if the key doesn't exist. * * @param string $key * @param mixed $value * @param int $minutes * @return bool */ public function add($key, $value, $minutes) { return $this->memcached->add($this->prefix.$key, $value, $minutes * 60); } /** * Increment the value of an item in the cache. * * @param string $key * @param mixed $value * @return int|bool */ public function increment($key, $value = 1) { return $this->memcached->increment($this->prefix.$key, $value); } /** * Decrement the value of an item in the cache. * * @param string $key * @param mixed $value * @return int|bool */ public function decrement($key, $value = 1) { return $this->memcached->decrement($this->prefix.$key, $value); } /** * Store an item in the cache indefinitely. * * @param string $key * @param mixed $value * @return void */ public function forever($key, $value) { $this->put($key, $value, 0); } /** * Remove an item from the cache. * * @param string $key * @return bool */ public function forget($key) { return $this->memcached->delete($this->prefix.$key); } /** * Remove all items from the cache. * * @return void */ public function flush() { $this->memcached->flush(); } /** * Get the underlying Memcached connection. * * @return \Memcached */ public function getMemcached() { return $this->memcached; } /** * Get the cache key prefix. * * @return string */ public function getPrefix() { return $this->prefix; } /** * Set the cache key prefix. * * @param string $prefix * @return void */ public function setPrefix($prefix) { $this->prefix = ! empty($prefix) ? $prefix.':' : ''; } } |
可以看到我们在构造函数中传入了Memcached实例,然后在此实例基础上具体实现接口所定义的方法,其他的实现类也是类似,这样通过Store接口,我们就将缓存代码和具体依赖解耦,方便后续扩展以及供其他人使用。比如这里我们定义一个CacheStore类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php namespace App\Tests; use Illuminate\Contracts\Cache\Store; class CacheStore { protected $store; public function __construct(Store $store) { $this->store = $store; } public function get($key) { return $this->store->get($key); } public function put($key, $value, $minutes=1) { $this->store->put($key, $value, $minutes); } public function forget($key) { $this->store->forever($key); } public function flush() { $this->store->flush(); } } |
然后我们可以在配置文件中配置使用的默认缓存驱动,比如Memcached,然后在代码中调用时这样使用:
1 2 3 4 5 6 |
$memcached = new \Memcached(); $memcached->addServer('localhost',11211); $memcachedCache = new MemcachedStore($memcached); $cacheStore = new CacheStore($memcachedCache); $cacheStore->put('site','<a href="http://laravelacademy.org/">http://LaravelAcademy.org</a>'); dd($cacheStore->get('site')); |
注:这里只是做简单演示,不要真的这么去使用Laravel提供的缓存功能,实际上Laravel底层对缓存处理要比我这里的演示代码优雅的多。 总之,使用接口编写的代码更灵活,能委托别人实现细节,使用接口后会有越来越多的人使用你的代码,因为他们只需要知道如何实现接口,就可以无缝地使用你的代码。实际上,我们在使用服务提供者和依赖注入时也是以这种面向接口编程为基础进行了更复杂的扩展而已。 from:http://laravelacademy.org/post/4246.html
View Details1、什么是命名空间 如果你只需要知道现代PHP特性中的一个,那就应该是命名空间。命名空间在PHP5.3.0中引入,其作用是按照一种虚拟的层次结构组织PHP代码,这种层次结构类似操作系统中文件系统的目录结构。命名空间是现代PHP组件生态的基础,现代的PHP组件框架代码都是放在各自全局唯一的厂商命名空间中,以免和其他厂商使用的常见类名冲突。 下面我来看看真实的PHP组件是如何使用命名空间的。Laravel框架中的Http组件用于管理HTTP请求和响应,这个组件用到了常见的类名,例如Request、Response,很多其他PHP组件也用到了这样的类名,既然其他PHP代码也用到了相同的类名,那怎么使用这个组件呢?其实我们可以放心使用,因为这个组件的代码放在了唯一的厂商命名空间Illuminate中。打开这个组件在GitHub中的仓库(https://github.com/laravel/framework/blob/master/src/Illuminate/Http/Response.php),找到Response.php文件: 第3行代码如下:
1 |
namespace Illuminate\Http; |
这一行是PHP命名空间声明语句,声明命名空间的代码始终应该放在<?php标签后的第一行。通过这个命名空间的声明语句我们可以看到Response位于厂商命名空间Illuminate中(最顶层命名空间),我们还看到Response类在子命名空间Http中,你可以看下和Response.php文件在同一层级的其他文件,会发现它们都使用相同的命名空间声明语句。 命名空间的作用是封装和组织相关的PHP类,就像在文件系统中把相关的文件放在同一个目录中一样。PHP命名空间和操作系统的物理文件系统不同,这是一个虚拟概念,没必要和文件系统中的目录结构完全相同,虽然如此,但是大多数PHP组件为了兼容广泛使用的PSR-4自动加载标准,会把命名空间放到对应文件系统的子目录中。 2、为什么使用命名空间 前面已经提到过,我们的代码可能和其他开发者的代码使用相同的类名、接口名、函数或常量名,如果不使用命名空间,名称会起冲突,导致PHP执行出错。而使用命名空间将代码放到唯一的厂商命名空间,我们的代码就可以和其他开发者使用相同的类名、接口名、函数或常量名。 当然如果你开发的是小型个人项目,只有少量的依赖,类名冲突可能不是问题,但是如果在团队中工作,开发用到许多第三方依赖的大型项目,就要认真对待命名冲突问题,因为你无法控制项目依赖在全局命名空间中引入的类、接口、函数和常量,这也是为什么要使用命名空间的原因。 3、声明命名空间 每个PHP类、接口、函数和常量都在命名空间中,声明命名空间很简单,在<?php标签后的第一行声明,声明语句以namespace开头,随后是一个空格,然后是命名空间的名称,最后以;结尾。 命名空间经常用于设置顶层厂商名,比如我们设置厂商名为LaravelAcademy:
1 2 |
<?php namespace LaravelAcademy; |
在这个命名空间声明语句后声明的所有PHP类、接口、函数和常量都在LaravelAcademy命名空间中,而且和Laravel学院有某种关系。如果我们想组织学院用到的代码该怎么做呢?答案是使用子命名空间。 子命名空间的声明方式和前面的示例完全一样,唯一的区别是我们要使用\符号把命名空间和子命名空间分开,例如:
1 2 |
<?php namespace LaravelAcademy\ModernPHP; |
这个命名空间后的所有类、接口、函数和常量都位于LaravelAcademy\ModernPHP中。 在同一个命名空间中的类没必要在同一个PHP文件中声明,可以在PHP文件的顶部指定一个命名空间或子命名空间,此时,这个文件的代码就是该命名空间或子命名空间的一部分。因此我们可以在不同文件中编写属于同一个命名空间的多个类。 注:厂商命名空间是最顶层的命名空间,也是最重要的命名空间,用于识别品牌或组织,必须具有全局唯一性。子命名空间相对而言没那么重要,但是可以用于组织项目的代码。 4、导入和别名 在命名空间出现之前,PHP开发者使用Zend风格的类名解决命名冲突问题,这是一种类的命名方案,因Zend框架而流行,这种命名方案在PHP类名中使用下划线的方式表示文件系统的目录分隔符。这种约定有两个作用:其一,确保类名是唯一的;其二,原生的自动加载器会把类名中的下划线替换成文件系统的目录分隔符,从而确定文件的路径。例如,Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query类对应的文件是Zend/Cloud/DocumentService/Adapter/WindowsAzure/Query.php。可以看出,这种命名有个缺点:类名特别长。 现代的PHP命名空间也有这个问题,例如上述Response类完整的全名是Illuminate\Http\Response,幸好,我们可以通过导入以及创建别名的方式来改变这一状况。 导入的意思是指,在每个PHP文件中告诉PHP想使用哪个命名空间、类、接口、函数和常量,导入后就不用使用全名了:
1 2 3 4 5 |
<?php use Illuminate\Http\Response; $response = new Response(‘Oops’, 400); $response->send(); |
我们通过use关键字告诉PHP,我们想使用Illuminate\Http\Response类,我们只需要输入一次完全限定的类名,随后实例化Response的时候,无需使用完整的类名。 如果觉得这样的类名还是长,可以创建别名。创建别名指的是告诉PHP我要使用简单的名称引用导入的类、接口、函数或常量:
1 2 3 4 5 6 |
<?php use Illuminate\Http\Response as Res; $res = new Res(‘Oops’, 400); $res->send(); |
从PHP 5.6开始还可以导入函数和常量,不过要调整use关键字的句法,如果要导入函数,需要使用use func:
1 2 3 4 |
<?php use func Namespace\functionName functionName(); |
如果想导入常量,可以使用use constant:
1 2 3 4 |
<?php use constant Namespace\CONST_NAME; echo CONST_NAME; |
当然也支持别名,创建方式和类一样。 5、实用技巧 多重导入 如果想要在PHP文件中导入多个类、接口、函数或常量,需要在PHP文件的顶部使用多个use语句,PHP支持用简短的语法把多个use语句写成一行:
1 2 3 |
<?php use Illuminate\Http\Request, Illuminate\Http\Response; |
但是为了可读性,建议不要这么写,还是一行写一个use语句比较好:
1 2 3 |
<?php use Illuminate\Http\Request; use Illuminate\Http\Response; |
一个文件使用多个命名空间 PHP允许在一个文件中定义多个命名空间:
1 2 3 4 5 6 7 8 |
<?php namespace Foo { //声明类、接口、函数、常量 } namespace Bar { //声明类、接口、函数、常量 } |
但这么做不好,违背了“一个文件一个类”的良好实践,因此不建议这么做。 全局命名空间 如果引用的类、接口、函数和常量没有指定命名空间,PHP假定引用的类、接口、函数和常量在当前的命名空间中。如果要使用其他命名空间的类、接口、函数或常量,需要使用完全限定的PHP类名(命名空间+类名)。 有些代码在全局命名空间中,没有命名空间,比如原生的Exception类就是这样。在命名空间中引用全局的代码时,需要在类、接口、函数或常量前加\符号:
1 2 3 4 5 6 7 8 |
<?php namespace My\App; class Foo { public function doSomething() { throw new \Exception(); } } |
自动加载 命名空间还为PHP-FIG制定的PSR-4自动加载标准奠定了坚实的基础,大多数现代的PHP组件都使用了这种自动加载模式,使用依赖管理器Composer可以自动加载项目的依赖,后续我们还会详细介绍Composer和PHP-FIG,现在你只需要知道没有命名空间,就没有现代的PHP生态系统和基于组件的新型架构,由此可见命名空间的重要性。 from:http://laravelacademy.org/post/4221.html
View Details请看解决方案: 1. 修改maven的settings.xml文件。 添加以下行,jdk版本改为自己需要的版本:
1 2 3 4 5 6 7 8 9 10 11 12 |
<profile> <id>jdk-1.7</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.7</jdk> </activation> <properties> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> <maven.compiler.compilerVersion>1.7</maven.compiler.compilerVersion> </properties> </profile> |
2. 在eclipse中检查配置文件是否被引用到,之前我的user settting使用的位置不对。 然后update setttings。 3. 最后选择创建的项目,更新项目后,引用的jdk版本自动刷新。 from:http://www.cnblogs.com/30go/p/7154298.html
View Details问题描述:eclipse加载新的项目后报一个错误,具体描述如下: Description Resource PathLocation Type Java compiler level does notmatch the version of the installed Java project facet.webattemp Unknown FacetedProject Problem (Java Version Mismatch) 问题分析: java版本不匹配:Facted Project 中的Java 版本设定与项目的Java 版本设定不一致。 问题解决: 在当前项目上点右键,属性--Project Facets中,配置编译版本与java compiler的版本一致。 1、选中项目后按下alt+enter组合键或者右键Project | Properties | Java Compiler(type filter text输入compiler可快速定位),如下图所示: 2、修改Project Facets的Java值,使之和Compiler compliance level相同:打开方式参考1,如下图: 总结: 刚开始用的是eclipse比较老的一个版本,常用的文件定位的插件装上就是识别不出来,换了一个高版本的eclipse加载项目后就报出上面的错误,因为是第一次见到这个错误,以为是用的这个项目不支持高版本的eclipse呢,头说不行就用那个老版本得了,不要在这个上浪费时间,可是提高班培养出来的习惯,这样感觉就是不爽,还是想查一下再说,上网一查,这样的问题很多,当然很快就找到了解决的办法,工欲善其事,必先利其器,有了好的工具开发的时候心情好多了,自然效率也就提高了。 参考:http://blog.163.com/luckcq@yeah/blog/static/171747707201172223546805/ from:http://blog.163.com/lfsyhuangaigang@126/blog/static/774366702012913113253907/
View Details