SegmentFault Experience With Laravel 最新的文章
2017-07-15T20:28:12+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
PHPStorm-Xdebug-Laravel 快速上手
https://segmentfault.com/a/1190000010201919
2017-07-15T20:28:12+08:00
2017-07-15T20:28:12+08:00
lcoding
https://segmentfault.com/u/qzuser_58592a1da62d9
5
<h2>Introduction</h2>
<p>xdebug是php调试的组件,是调试利器,在日常开发中排错时,其断点调试功能非常有帮助,使得我们开发者不用依赖于传统的var_dump()/echo,比较plain的调试。并且xdebug还有一个好处,通过xdebug(based on Phpstorm)我们还可以查看代码运行的逻辑,比如:要研究Laravel的源码,那么这时使用xdebug,将会达到事半功倍的效果。好了,闲话就扯这么多,Let's go!</p>
<h2>Requirements</h2>
<ol>
<li><p>系统环境:win10</p></li>
<li><p>PHPStorm 2016.3 <a href="https://link.segmentfault.com/?enc=rgNcXTTZg1%2BBiq%2FQ69CX%2BA%3D%3D.L8viFOH39TznohB9lRoylPdkUD6Cp6c2Hlqm5NHlC2ndFOhHT1s3RcKRqwUuBcLyAlbE%2Bb5XiTZLF5GgnAx5Xg%3D%3D" rel="nofollow">下载地址</a></p></li>
<li><p>Xmapp集成环境(php7) <a href="https://link.segmentfault.com/?enc=AU7I%2FW4A044YWsrGcP2WJg%3D%3D.qtJysyt%2B1lzrLb%2B4%2FOD1cidPzAh85h3y6%2BYggJKiME62xEFXUakvFEM9r1i2GOqW" rel="nofollow">下载地址</a></p></li>
<li><p>Laravel5.4.28</p></li>
</ol>
<h2>Xdebug</h2>
<blockquote><p>下载地址 : <a href="https://link.segmentfault.com/?enc=rakIRbtPz69xp%2BfF9PhXzQ%3D%3D.OMeoKchAKJAum9s0f8nbolUZSJkTnEy3DzWDGd438mg%3D" rel="nofollow">https://xdebug.org/download.php</a></p></blockquote>
<h2>不知道下载哪个版本?没关系,先打印phpinfo</h2>
<p><img src="/img/bVQX16?w=1396&h=874" alt="clipboard.png" title="clipboard.png"></p>
<h2>2.点击图中的超链接</h2>
<p><img src="/img/bVQX1Y?w=1527&h=871" alt="clipboard.png" title="clipboard.png"></p>
<h2>3.粘贴phpinfo信息</h2>
<p><img src="/img/bVQX2q?w=1253&h=886" alt="clipboard.png" title="clipboard.png"></p>
<h2>4.点击下载Dll文件</h2>
<p><img src="/img/bVQX2t?w=1285&h=880" alt="clipboard.png" title="clipboard.png"></p>
<h2>5.将dll文件放置php安装目录的ext目录中并重命名为php_xdebug.dll</h2>
<p><img src="/img/bVQX3z?w=1097&h=877" alt="clipboard.png" title="clipboard.png"></p>
<h2>6.配置php.ini</h2>
<p><img src="/img/bVQX34?w=981&h=352" alt="clipboard.png" title="clipboard.png"></p>
<pre><code>[Xdebug]
zend_extension="php_xdebug.dll路径" //其他配置项不用动,修改此路径即可
xdebug.remote_enable=1
xdebug.remote_port=9000 //默认端口
xdebug.remote_host=localhost
xdebug.profiler_enable=1
xdebug.remote_mode = "req"
xdebug.trace_output_dir="./xdebug"
xdebug.profiler_output_dir="./xdebug"
xdebug.remote_handler="dbgp"
xdebug.idekey = "phpstorm" //必填</code></pre>
<h3>7.ok,重启Xampp.</h3>
<h2>PHPStorm</h2>
<p>新建一个laravel项目,打开'PS',按热键 'Ctrl+ Alt+ S</p>
<h3>1.PHPunit 配置</h3>
<p><img src="/img/bVQX5W?w=1777&h=964" alt="clipboard.png" title="clipboard.png"></p>
<h3>2.Xdebug 配置</h3>
<p>别忘了'Apply'<br><img src="/img/bVQX6h?w=1777&h=974" alt="clipboard.png" title="clipboard.png"></p>
<p><strong>点击'Generate'</strong></p>
<p><img src="/img/bVQX6v?w=1897&h=855" alt="clipboard.png" title="clipboard.png"></p>
<p><img src="/img/bVQX6B?w=1736&h=923" alt="clipboard.png" title="clipboard.png"></p>
<h2>调试</h2>
<h3>1.断点,开启监听</h3>
<p><img src="/img/bVQX7S?w=1920&h=1020" alt="clipboard.png" title="clipboard.png"></p>
<h3>2.打开浏览器,如图</h3>
<p><img src="/img/bVQX8n?w=1717&h=145" alt="clipboard.png" title="clipboard.png"></p>
<h3>2.ps弹窗,如图</h3>
<p><img src="/img/bVQX8C?w=771&h=750" alt="clipboard.png" title="clipboard.png"></p>
<h3>3.debug Info 如图</h3>
<p><img src="/img/bVQX8H?w=1920&h=1020" alt="clipboard.png" title="clipboard.png"></p>
<hr>
<h2>Conclusion</h2>
<ol>
<li><p>xdebug,是调试利器,也应该是php developer的调试必备,但也发现phpstorm本身很重,消耗的系统内存也是比较大,导致部分phper不大用phpstrom,当然xdebug也就用的少了.</p></li>
<li><p>Hope all can happy coding!</p></li>
</ol>
Debugging collections(译)
https://segmentfault.com/a/1190000009785609
2017-06-15T01:42:59+08:00
2017-06-15T01:42:59+08:00
lcoding
https://segmentfault.com/u/qzuser_58592a1da62d9
0
<blockquote><p>注:本文是翻译Freek Van der Herten写的关于Collection<br>调试技巧,,读完以后很实用,分享给大家.阅读过程中,翻译有错误的希望大家指正,thx.. <br>原文链接: <a href="https://link.segmentfault.com/?enc=ti9qeyTC6%2BpE%2F%2F0S1rdHZw%3D%3D.fuAOWdizFqaqkeDuxFByWT%2FWNls1n6sQlhkX0yKx5iO%2FMQ6A0rAD7PD0sEUtyMyz" rel="nofollow">https://murze.be/2016/06/debu...</a></p></blockquote>
<h4>最近我一直在使用Laravel的collection,如果你还不了解collection,我简单说下:一个collection(集合)就是一个‘功能强大’的数组,有很多强大处理其内部数据的函数.但是唯一让我头疼的地方是如何调试collection的链式操作,以下是我改善过的业务处理.</h4>
<h4>使用Collection类你可以使用链式的方式,通过操作函数来处理数据,举个例子如下(为了让代码更简短,我把函数体中的具体实现给省略了)</h4>
<pre><code>collect($items)
->filter(function() {
...
})
->unique(function() {
...
})
->map(function() {
...
})
->sortBy(function() {
...
});
</code></pre>
<h4>想象一下如果你需要调试这段代码,你想要获取map函数的返回值,你会怎么做?那么你可能会用dd()把这'整个操作'包起来,得出返回值.</h4>
<pre><code> dd(collect($items)
->filter(function() {
...
})
->unique(function() {
...
})
->map(function() {
...
}))
->sortBy(function() {
...
});</code></pre>
<h4>的确,这样足够了,整个操作的返回值也能打印出来,但是还是有点琐碎,你必须要要在每次测试操作的开始和结尾加点东西(开头:'dd(',结尾:')'),我觉得这样可读性上不太好.并且移动dd语句测试也是很麻烦,结尾的括号又特别容易被忽略,so,虽然我看似解决了这个问题,但是我敢保证如果像这样调试的话,你很快就觉得很繁琐.ok,其实是有解决方案的.</h4>
<h4>laravel的Collection类是Macroable(暂且译为:可扩展的),这意味着我们可以在运行时添加相应的函数.为了提高我们调试流程,我们可以创建简单的dd macro:</h4>
<pre><code>Collection::macro('dd', function () {
dd($this);
});</code></pre>
<h4>为了能在项目中使用这个函数,需要将以上代码放到服务提供者中.ok,如下,看下这个<a href="https://link.segmentfault.com/?enc=hLOiap3BZUOVa1u6B03Fsw%3D%3D.%2BKYwp%2Fvsorr6wMd%2BnFyaolhRQS4LLqlFPc9EWiiawW3e7YrLa5VPh%2FM8Nmgj0WDZ" rel="nofollow">Blender</a>--我们的laravel模板,<a href="https://link.segmentfault.com/?enc=JUwWxPHwxH6hJP%2Fup330hw%3D%3D.ev%2BbVKkks6YeLOOANIXMNQ%2F6pRU%2FeL67hbiK9ipxxumIGqYeihdrBBZjhkFkTVotUXp8UyGqTNaExM2iXVBBIXAfBJ96YNV6%2F6gdMJx8GRzgijTECz4pbpvPlLh%2BN0kg" rel="nofollow">举个例子</a>
</h4>
<h4>使用macro debugging调试Collection,对于我们来说变得很简单了.ok,我们来看看它如何使用.</h4>
<pre><code>collect($items)
->filter(function() {
...
})
->unique(function() {
...
})
->map(function() {
...
})
->dd()
->sortBy(function() {
...
});</code></pre>
<h4>为了测试特定链式操作的返回值,你可以将'->dd()'放到该操作的最后即可,这样一来,相比较直接'dd(....)',可读性就高了.在完成调试后,我们只需把链式操作尾部的代码去掉即可.还有你可用使用PHPStrom的快捷键'alt+shift+arrow-up' 和'alt+shift+arrow-down'可以将'->dd()'部分更方便移到前一个或下一个链式操作之后.</h4>
<h2>结语</h2>
<h4>你还可以去看Laravel的官方文档和<a href="https://link.segmentfault.com/?enc=v6buoBpvetA7nh1fO748Jg%3D%3D.bVsBYri0phKhU3SD0JmYkHMtg2DZVoDZzlPUDuJtEjcwHqHpqnj6m%2BljE6Cb61yKshVwVTOXUIk97qVt9fbmAQ%3D%3D" rel="nofollow">Refactoring to Collection</a>
</h4>
<h4>试读篇章中文版:<a href="https://segmentfault.com/a/1190000009079722">Refactoring to Collection</a>
</h4>
laravel5.2和redis_cluster配置
https://segmentfault.com/a/1190000009691518
2017-06-07T15:21:13+08:00
2017-06-07T15:21:13+08:00
lcoding
https://segmentfault.com/u/qzuser_58592a1da62d9
2
<h2><strong>纲要:</strong></h2>
<ul>
<li><p><strong>laravel中redis集群的应用</strong></p></li>
<li><p><strong>predis对redis集群模式的底层实现</strong></p></li>
</ul>
<h2><strong>laravel中redis集群的应用</strong></h2>
<p>这部分我想分享下laravel5.2中redis集群的配置(官网也有redis集群的配置讲解,但是5.2版还是有点不足,只是说了将cluster配置项设为true,但光这样一个选项不能代表,一个新手直接可用redis集群,这部分还包括predis客户端的事,所以后面我也会分享下关于predis的源码分析)。</p>
<p>redis—cluster的搭建:<a href="https://segmentfault.com/a/1190000009689151">Easy Building Redis-cluster (轻松搭建reids集群)</a></p>
<p><strong>系统软件清单:</strong></p>
<ul>
<li><p>laravel版本:5.2</p></li>
<li><p>redis版本:>=3.0<a href="https://link.segmentfault.com/?enc=vuayUzUhB5sui1XKewzHDQ%3D%3D.5HUR9ELeOrseHx9CJOdAlPoid3qc7Iyu1%2FINu6Qij5c%3D" rel="nofollow">下载最新版即可</a></p></li>
<li><p>predis:>=1.0<a href="https://link.segmentfault.com/?enc=StcpSRQh%2BDHCA7U%2BJ7SaFw%3D%3D.HKn%2B95BFgp5025ZiSZR50Ul4dgK2FfUDZvT5hUgVRhJMbjkN230xALWadQBsQx03" rel="nofollow">下载地址</a></p></li>
</ul>
<p><strong>配置文件:config/database.php</strong></p>
<pre><code>'redis' => [
'cluster' => env('REDIS_CLUSTER', true),
**'options'=>['cluster'=>'redis']**, //官网没说这个,这是必须的!!后面会讲为什么这么配?
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'), //任选一个master节点
'port' => env('REDIS_PORT',6379),
'database' => 0,
'timeout'=>15,
'read_write_timeout'=>1800 //redis客户端连接以后的读写超时时间(默认是60s)
],
'extra'=>[
'host'=>env('REDIS_EXTRA_HOST','127.0.0.1'), //任意一个集群中的节点即可
'port'=>env('REDIS_EXTRA_PORT',7001)
]
]
</code></pre>
<p>ok,配完上面的步骤,redis集群就可以用了. <br>具体使用redis集群的应用场景根据业务需求有很多种,比如集群存session等.<br>app('request')->session()->put('key','value');就存到集群中了.</p>
<h2><strong>predis对redis集群的底层实现</strong></h2>
<p>ok,想要了解配置文件中的参数,还是得看源代码,当然也是predis,上代码.</p>
<pre><code>Illuminate\Support\ServiceProvider\RedisServiceProvider;
public function register()
{
$this->app->singleton('redis', function ($app) {
return new Database($app['config']['database.redis']);
});
}
Illuminate\Redis\Database;
public function __construct(array $servers = [])
{
$cluster = Arr::pull($servers, 'cluster'); //获取'cluster'的键值
$options = (array) Arr::pull($servers, 'options');
//options 就是database.php中'options'的键值,是一个数组(但官网没有提到,是个坑.)
if ($cluster) {
$this->clients = $this->createAggregateClient($servers, $options); //集群模式'cluster=true'
} else {
$this->clients = $this->createSingleClients($servers, $options); //单机模式 'cluster=false'
}
}
protected function createAggregateClient(array $servers, array $options = [])
{
return ['default' => new Client(array_values($servers), $options)]; //predis的Client类
}
----------
注意:这里提醒一下各参数的值:
此时$servers=[
[
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT',6379),
'database' => 0,
'timeout'=>15,
'read_write_timeout'=>1800
],
[
'host'=>env('REDIS_EXTRA_HOST','127.0.0.1'),
'port'=>env('REDIS_EXTRA_PORT',7001)
]
]
$options = ['cluster'=>'redis']
其实到这儿,就可以解释在database.php中增加options选项,而且是必选项,因为底层代码需要判断数据切片的方式.
除了看源码,
predis的包文档也做了解释.https://packagist.org/packages/predis/predis
-------</code></pre>
<p>接下来我们看看这些底层要初始化的类吧.</p>
<pre><code> Predis\Client;
public function __construct($parameters = null, $options = null)
{
$this->options = $this->createOptions($options ?: array());
#$this->connection = $this->createConnection($parameters ?: array());
#$this->profile = $this->options->profile;
}
protected function createOptions($options)
{
if (is_array($options)) {
return new Options($options); //如你所见,实例化Options类
}
if ($options instanceof OptionsInterface) {
return $options;
}
throw new \InvalidArgumentException('Invalid type for client options.');
}
public function __construct(array $options = array())
{
$this->input = $options;
$this->options = array();
$this->handlers = $this->getHandlers();
}
</code></pre>
<blockquote><p>$this->connection = $this->createConnection($parameters ?: array())</p></blockquote>
<pre><code>Predis\Client 文件
protected function createConnection($parameters)
{
# if ($parameters instanceof ConnectionInterface) {
# return $parameters;
# }
# if ($parameters instanceof ParametersInterface || is_string($parameters)) {
# return $this->options->connections->create($parameters);
# }
# if (is_array($parameters)) {
# if (!isset($parameters[0])) {
# return $this->options->connections->create($parameters);
# }
$options = $this->options;
# if ($options->defined('aggregate')) {
# $initializer = $this->getConnectionInitializerWrapper($options->aggregate);
# $connection = $initializer($parameters, $options);
# } else {
# if ($options->defined('replication') && $replication = $options->replication) {
# $connection = $replication;
# } else {
$connection = $options->cluster; //
# }
$options->connections->aggregate($connection, $parameters);
# }
return $connection;
# }
# if (is_callable($parameters)) {
# $initializer = $this->getConnectionInitializerWrapper($parameters);
# $connection = $initializer($this->options);
# return $connection;
# }
# throw new \InvalidArgumentException('Invalid type for connection parameters.');
}
Predis\Configuration\Options;
protected function getHandlers()
{
return array(
'cluster' => 'Predis\Configuration\ClusterOption',
'connections' => 'Predis\Configuration\ConnectionFactoryOption',
#'exceptions' => 'Predis\Configuration\ExceptionsOption',
#'prefix' => 'Predis\Configuration\PrefixOption',
#'profile' => 'Predis\Configuration\ProfileOption',
#'replication' => 'Predis\Configuration\ReplicationOption',
);
}
public function __get($option)
{
#if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
# return $this->options[$option];
#}
if (isset($this->input[$option]) || array_key_exists($option, $this->input)) {
$value = $this->input[$option];
unset($this->input[$option]);
# if (is_object($value) && method_exists($value, '__invoke'){
# $value = $value($this, $option);
# }
if (isset($this->handlers[$option])) {
$handler = $this->handlers[$option];
$handler = new $handler(); //会实例化Predis\Configuration\ClusterOption类
$value = $handler->filter($this, $value);
}
return $this->options[$option] = $value;
}
# if (isset($this->handlers[$option])) {
# return $this->options[$option] = $this->getDefault($option);
# }
# return;
}
Predis\Configuration\ClusterOption文件
public function filter(OptionsInterface $options, $value)
{
if (is_string($value)) {
$value = $this->createByDescription($options, $value);
}
# if (!$value instanceof ClusterInterface) {
# throw new \InvalidArgumentException(
# "An instance of type 'Predis\Connection\Aggregate\ClusterInterface' was expected."
# );
# }
return $value;
}
protected function createByDescription(OptionsInterface $options, $id)
{
switch ($id) {
* Abstraction for a cluster of aggregate connections to various Redis servers
* implementing client-side sharding based on pluggable distribution strategies.
# case 'predis':
# case 'predis-cluster':
# return new PredisCluster();
//这个模式是客户端通过CRC16算法在客户端进行数据切片,
显然这种模式的集群是脆弱的,如果一个master节点挂了,
那其备节点若也挂了,那么获取数据就成问题了;
再有这种模式扩展性很差,维护成本高,
因此这个模式不推荐.当然用最新predis不存在这个问题.
我这边predis,1.0算比较老了.
case 'redis':
case 'redis-cluster':
return new RedisCluster($options->connections);
//这种模式是基于服务端的数据切片,相较于第一种模式,优点也显而易见,维护成本低,扩展性好等.
default:
return;
}
}
public function __get($option)
{
#if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
# return $this->options[$option];
#}
# if (isset($this->input[$option]) || array_key_exists($option, $this->input)) {
# $value = $this->input[$option];
# unset($this->input[$option]);
# if (is_object($value) && method_exists($value, '__invoke'){
# $value = $value($this, $option);
# }
# if (isset($this->handlers[$option])) {
# $handler = $this->handlers[$option];
# $handler = new $handler();
# $value = $handler->filter($this, $value);
# }
# return $this->options[$option] = $value;
#}
if (isset($this->handlers[$option])) { //$options='connections'
return $this->options[$option] = $this->getDefault($option);
# }
# return;
}
public function getDefault($option)
{
if (isset($this->handlers[$option])) {
$handler = $this->handlers[$option]; //$handler = 'Predis\Configuration\ConnectionFactoryOption';
$handler = new $handler();
return $handler->getDefault($this);
}
}
Predis\Configuration\ConnectionFactoryOption文件
public function getDefault(OptionsInterface $options)
{
return new Factory(); //最后实例化了一个'工厂'类
}
</code></pre>
<blockquote><p>$this->profile = $this->options->profile;</p></blockquote>
<pre><code>Predis\Configuration\ProfileOption文件
public function __get($option)
{
#if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
# return $this->options[$option];
#}
# if (isset($this->input[$option]) || array_key_exists($option, $this->input)) {
# $value = $this->input[$option];
# unset($this->input[$option]);
# if (is_object($value) && method_exists($value, '__invoke'){
# $value = $value($this, $option);
# }
# if (isset($this->handlers[$option])) {
# $handler = $this->handlers[$option];
# $handler = new $handler();
# $value = $handler->filter($this, $value);
# }
# return $this->options[$option] = $value;
#}
if (isset($this->handlers[$option])) { //$options='profile'
return $this->options[$option] = $this->getDefault($option);
# }
# return;
}
public function getDefault($option)
{
if (isset($this->handlers[$option])) {
$handler = $this->handlers[$option]; //$handler = 'Predis\Configuration\ProfileOption';
$handler = new $handler();
return $handler->getDefault($this);
}
}
Predis\Configuration\ProfileOption文件
public function getDefault(OptionsInterface $options)
{
$profile = Factory::getDefault(); //实例化了Predis\Profile\RedisVersion300类
$this->setProcessors($options, $profile);
return $profile;
}
</code></pre>
Refactoring to collection(译)
https://segmentfault.com/a/1190000009079722
2017-04-16T00:41:33+08:00
2017-04-16T00:41:33+08:00
lcoding
https://segmentfault.com/u/qzuser_58592a1da62d9
5
<p>《Refactoring To Collection》</p>
<h5>本文是翻译Adam Wathan 的《Collection To Refactoring》的试读篇章,这篇文章内容不多,但是可以为我们Laraver使用者能更好使用collection提供了可能性,非常值得看看。虽然是试读部分,但是Wathan还是很有诚意的,试读文章还是能学到东西的,但是遗憾的是,我大概搜了下,目前好像还没有中文版,为此,我决定翻译这篇文章,让英文不太好的朋友,可以学习这篇文章。</h5>
<pre><code>获取试读文章:https://adamwathan.me/refactoring-to-collections/#sample
</code></pre>
<h2>高阶函数</h2>
<p>高阶函数就是参数为可以为function,并且返回值也可为function的函数。我们举一个用高阶函数实现数据库事务的例子.代码如下:</p>
<pre><code>
public function transaction($func)
{
$this->beginTransaction();
try {
$result = $func();
$this->commitTransaction();
} catch (Exception $e) {
$this->rollbackTransaction(); throw $e;
}
return $result;
}
</code></pre>
<p>看下它的使用:</p>
<pre><code> try {
$databaseConnection->transaction(function () use ($comment) {
$comment->save();
});
} catch (Exception $e) {
echo "Something went wrong!";
}
</code></pre>
<h2>Noticing Patterns(注意模式)</h2>
<p>高阶函数是非常强大的,因为我们可以通过它把其他编程模式下所不能重用的部分逻辑给抽象出来。<br>比方说,我们现在有顾客名单,但我们需要得到他们的邮箱地址.我们现在不用高阶函数,用一个foreach来实现它,代码如下。</p>
<pre><code> $customerEmails = [];
foreach ($customers as $customer) {
$customerEmails[] = $customer->email;
}
return $customerEmails;</code></pre>
<p>现在我们有一批商品库存,我们想知道每种商品的总价,我们可能会这样处理:</p>
<pre><code> $stockTotals = [];
foreach ($inventoryItems as $item) {
$stockTotals[] = [ 'product' => $item->productName, 'total_value' =>$item->quantity * $item->price, ];
}
return $stockTotals;</code></pre>
<p>乍看之下,两个例子可能不太一样,但是把它们再抽象一下,如果你仔细观察,你会意识到其实两个例子之间只有一点是不一样的.</p>
<p>在这两个例子中,我们做的只是对数组中的每个元素进行相应的操作再将其赋给一个新数组.两个例子真正的不同点在于我们对数组元素的处理不一样。<br>在第一个例子中,我们需要'email'属性。</p>
<pre><code> # $customerEmails = [];
#foreach ($customers as $customer) {
$email = $customer->email;
#$customerEmails[] = $email;
#}
#return $customerEmails;</code></pre>
<p>在第二个例子中,我们用$item中的几个字段创建了一个新的关联数组.</p>
<pre><code> # $stockTotals = [];
#foreach ($inventoryItems as $item) {
$stockTotal = [
'product' => $item->productName,
'total_value' => $item->quantity * $item->price,
];
# $stockTotals[] = $stockTotal;
# }
# return $stockTotals;
</code></pre>
<p>我们把两个例子的逻辑处理简化一下,我们可以得到如下代码:</p>
<pre><code> $results = [];
foreach ($items as $item) {
# $result = $item->email;
$results[] = $result;
}
return $results;</code></pre>
<pre><code> $results = [];
foreach ($items as $item) {
# $result = [
# 'product' => $item->productName,
# 'total_value' => $item->quantity * $item->price,
# ];
$results[] = $result;
}
return $results;</code></pre>
<p>我们现在接近抽象化了,但是中间那两个代码还在防碍着我们进行下一步操作.我们需要将這两部分取出来,然后用使得两个例子保持不变的东西来代替他们.</p>
<p>我们要做的就是把这两个代码放到匿名函数中,每个匿名函数会将每个数组元素作为其参数,然后进行相应的处理并且将其返回.</p>
<p>以下是用匿名函数处理email的实例:</p>
<pre><code> $func = function ($customer) {
return $customer->email;
};
#$results = [];
#foreach ($items as $item) {
$result = $func($item);
#$results[] = $result;
#}
#return $results;</code></pre>
<p>以下用匿名函数的商品库存实例:</p>
<pre><code> $func = function ($item) {
return [
'product' => $item->productName,
'total_value' => $item->quantity * $item->price,
];
};
#$results = [];
#foreach ($items as $item) {
$result = $func($item);
#$results[] = $result;
#}
#return $results;</code></pre>
<p>现在我们看到两个例子中有很多相同的代码我们可以提取出来重用,如果我们将其运用到自己的函数中,我们可以实现一个更高阶的函数叫map();</p>
<pre><code> function map($items, $func)
{
$results = [];
foreach ($items as $item) {
$results[] = $func($item);
}
return $results;
}
$customerEmails = map($customers, function ($customer) {
return $customer->email;
});
$stockTotals = map($inventoryItems, function ($item) {
return [
'product' => $item->productName,
'total_value' => $item->quantity * $item->price,
];
});
</code></pre>
<h2>Functional Building Blocks(功能构件块)</h2>
<p>map()函数是强大的处理数组的高阶函数中的一种,之后的例子中我们会讲到这部分,但是现在让我们来深入了解下基础知识。</p>
<h3>Each</h3>
<p>Each只是一个foreach循环嵌套一个高阶函数罢了,如下:</p>
<pre><code> function each($items, $func)
{
foreach ($items as $item) {
$func($item);
}
}
</code></pre>
<p>你或许会问你自己:"为什么会很厌烦写这个逻辑?"它隐藏了循环的详细实现(并且我们讨厌写循环逻辑).</p>
<p>假如PHP没有foreach循环,那each()实现方式就会变成这样:</p>
<pre><code>
function each($items, $func)
{
for ($i = 0; $i < count($items); $i++) {
$func($items[$i]);
}
}</code></pre>
<p>如果是没有foreach,那么就需要把对每个数组元素的处理进行抽象化.代码就会变成这样:</p>
<pre><code> for ($i = 0; $i < count($productsToDelete); $i++) {
$productsToDelete[$i]->delete();
}</code></pre>
<p>把它重写一下,让它变得更富有表达力.</p>
<pre><code> each($productsToDelete, function ($product) {
$product->delete();
});</code></pre>
<p>一旦你上手了链式功能操作,Each()在使用foreach循环时会有明显的提升,这部份我们会在之后讲到.</p>
<h4>在使用Each()有几件事需要注意下:</h4>
<ul><li><p>如果你想获得集合中的某个元素,你不应该使用Each()</p></li></ul>
<pre><code> // Bad! Use `map` instead.
each($customers, function ($customer) use (&$emails) {
$emails[] = $customer->email;
});
// Good!
$emails = map($customers, function ($customer) {
return $customer->email;
});</code></pre>
<ul><li><p>不像其他的数组处理函数,each不会返回任何值.由此可得,Each适合于执行一些逻辑处理,比如说像'删除商品','装货单','发送邮件',等等.</p></li></ul>
<pre><code> each($orders, function ($order) {
$order->markAsShipped();
});</code></pre>
<h3>MAP</h3>
<p>我们在前文多次提到过map(),但是它是一个很重要的函数,并且需要专门的章节来介绍它.<br>map()通常用于将一个数组中的所有元素转移到另一个数组中.将一个数组和匿名函数作为参数,传递给map,map会对数组中的每个元素用这个匿名进行处理并且将其放到同样大小的新数组中,然后返回这个新数组.</p>
<p>看下map()实现代码:</p>
<pre><code> function map($items, $func)
{
$result = [];
foreach ($items as $item) {
$result[] = $func($item);
}
return $result;
}</code></pre>
<h4>记住,新数组中的每个元素和原始数组中的元素是一一对应的关系。还有要理解map()是如何实现的,想明白:旧数组和新数组的每个元素之间存在一个映射关系就可以了.</h4>
<h4>Map对以下这些场景是非常适用的:</h4>
<ul><li><p>从一个对象数组中获取一个字段 ,比如获取顾客的邮件地址.</p></li></ul>
<pre><code> $emails = map($customers, function ($customer) {
return $customer->email;
});</code></pre>
<blockquote><p>Populating an array of objects from raw data, like mapping an array of JSON results into an array of domain objects</p></blockquote>
<pre><code> $products = map($productJson, function ($productData) {
return new Product($productData);
});</code></pre>
<ul><li><p>改变数组元素的格式,比如价格字段,其单位为"分",那么对其值进行格式化处理.<br> (如:1001 ==> 1,001这种格式).</p></li></ul>
<pre><code> $displayPrices = map($prices, function ($price) {
return '$' . number_format($price / 100, 2);
});</code></pre>
<h3>Map vs Each</h3>
<p>大部分人会对 "应该使用map"还是"使用each"犯难.</p>
<p>想下我们在前文用each做过商品删除的那个例子,你照样可以用map()去实现,并且效果是一样的.</p>
<pre><code> map($productsToDelete, function ($product) {
$product->delete();
});
</code></pre>
<p>尽管代码可以运行成功,但是在语义上还是不正确的.我们不能什么都用map(),因为这段代码会导致创建一个完全没用处的,元素全为null的数组,那么这就造成了"资源浪费",这是不可取的.</p>
<p>Map是将一个数组转移到另一个数组中.如果你不是转移任何元素,那么你就不应该使用map.</p>
<p>一般来讲,如果满足以下条件你应该使用each而不是map:</p>
<ol>
<li><p>你的回掉函数不会返回任何值.</p></li>
<li><p>你不会对map()返回的数组进行任何处理.</p></li>
<li><p>你只是需要每个数组的元素执行一些操作.</p></li>
</ol>
<h3>What's Your GitHub Score?</h3>
<p>这儿有一份某人在Reddit分享的面试问题.<br>GitHub提供一个开放的API用来返回一个用户最近所有的公共活动.响应会以json个返回一个对象数组,如下:</p>
<pre><code>[
{
"id": "3898913063",
"type": "PushEvent",
"public": true,
"actor": "adamwathan",
"repo": "tightenco/jigsaw",
"payload": { /* ... */ }
},
// ...
]
</code></pre>
<p>你可以用你的GitHub账号,试下这个接口:</p>
<blockquote><p><a href="https://link.segmentfault.com/?enc=6vidEXAgtlBTm2I6yXiCjw%3D%3D.YRMavAXB2dwkNbL4GnZMheJ%2BBHmYTteqT3CfCSQ2308%3D" rel="nofollow">https://api.github.com/users/</a>{your-username}/events</p></blockquote>
<p>面试问题是:获取这些事件并且决定一个用户的"GitHubd Score",基于以下规则:</p>
<ol>
<li><p>每个"PushEvent",5分.</p></li>
<li><p>每个"CreateEvent",4分.</p></li>
<li><p>每个"IssueEvent",3分.</p></li>
<li><p>每个'CommitCommentEvent',2分.</p></li>
<li><p>其他所有的事件都是1分.</p></li>
</ol>
<h3>Loops and Conditionals (循环和条件)</h3>
<p>首先让我们采用用命令式编程来解决这个问题.</p>
<pre><code> function githubScore($username)
{
// Grab the events from the API, in the real world you'd probably use
// Guzzle or similar here, but keeping it simple for the sake of brevity.
$url = "https://api.github.com/users/{$username}/events";
$events = json_decode(file_get_contents($url), true);
// Get all of the event types
$eventTypes = [];
foreach ($events as $event) {
$eventTypes[] = $event['type'];
}
// Loop over the event types and add up the corresponding scores
$score = 0;
foreach ($eventTypes as $eventType) {
switch ($eventType) {
case 'PushEvent':
$score += 5;
break;
case 'CreateEvent':
$score += 4;
break;
case 'IssuesEvent':
$score += 3;
break;
case 'CommitCommentEvent':
$score += 2;
break;
default:
$score += 1;
break;
}
}
return $score;
}</code></pre>
<p>Ok,让我们来"clean"(清理)下这块代码.</p>
<h3>Replace Collecting Loop with Pluck(用pluck替换collection的循环)</h3>
<p>首先,让我们把GitHub events 放到一个collection中.</p>
<pre><code> function githubScore($username)
{
$url = "https://api.github.com/users/{$username}/events";
- $events = json_decode(file_get_contents($url), true);
+ $events = collect(json_decode(file_get_contents($url), true));
// ...
}
</code></pre>
<p>Now,让我们看下第一次循环:</p>
<pre><code> #function githubScore($username)
#{
#$url = "https://api.github.com/users/{$username}/events";
#$events = collect(json_decode(file_get_contents($url), true));
$eventTypes = [];
foreach ($events as $event) {
$eventTypes[] = $event['type'];
}
#$score = 0;
#foreach ($eventTypes as $eventType) {
switch ($eventType) {
case 'PushEvent':
$score += 5;
break;
// ...
}
}
return $score;
}</code></pre>
<p>我们知道,任何时候我们要转移一个数组的每个元素到另外一个数组,可以用map是吧?在这种情况下,"转移"是非常简单的,我们甚至可以使用pluck,所以我们把它换掉.</p>
<pre><code> #function githubScore($username)
#{
#$url="https://api.github.com/users/{$username}/events";
#$events = collect(json_decode(file_get_contents($url), true));
$eventTypes = $events->pluck('type');
#$score = 0;
#foreach ($eventTypes as $eventType) {
#switch ($eventType) {
#case 'PushEvent':
#$score += 5;
# break;
# // ...
# }
# }
#return $score;
#}
</code></pre>
<p>嗯,少了四行代码,代码更有表达力了,nice!</p>
<h3>Extract Score Conversion with Map</h3>
<p>那么switch这块怎么处理呢?</p>
<pre><code> # function githubScore($username)
# {
# $url = "https://api.github.com/users/{$username}/events";
# $events = collect(json_decode(file_get_contents($url), true));
# $eventTypes = $events->pluck('type');
# $score = 0;
foreach ($eventTypes as $eventType) {
switch ($eventType) {
case 'PushEvent':
$score += 5;
break;
case 'CreateEvent':
$score += 4;
break;
case 'IssuesEvent':
$score += 3;
break;
case 'CommitCommentEvent':
$score += 2;
break;
default:
$score += 1;
break;
}
}
return $score;
}
</code></pre>
<p>我们现在要计算所有成绩的总和,但是我们用的是事件类型的集合(collection).</p>
<p>或许我们用成绩的集合去计算总成绩会更简单吗?让我们用map把事件类型转变为成绩,之后饭后该集合的总和.</p>
<pre><code> function githubScore($username)
{
$url ="https://api.github.com/users/{$username}/events";
$events = collect(json_decode(file_get_contents($url), true));
$eventTypes = $events->pluck('type');
$scores = $eventTypes->map(function ($eventType) {
switch ($eventType) {
case 'PushEvent':
return 5;
case 'CreateEvent':
return 4;
case 'IssuesEvent':
return 3;
case 'CommitCommentEvent':
return 2;
default:
return 1;
}
});
return $scores->sum();
}
</code></pre>
<p>这样看起来好一点了,但是switch这块还是让人不太舒服.再来.</p>
<h3>Replace Switch with Lookup Table("映射表"替换switch)</h3>
<p>如果你在开发过程中碰到类似的switch,那么你完全可以用数组构造"映射"关系.</p>
<pre><code> #function githubScore($username)
{
$url = "https://api.github.com/users/{$username}/events";
#$events = collect(json_decode(file_get_contents($url), true));
#$eventTypes = $events->pluck('type');
#$scores = $eventTypes->map(function ($eventType) {
$eventScores = [
'PushEvent' => 5,
'CreateEvent' => 4,
'IssuesEvent' => 3,
'CommitCommentEvent' => 2,
];
return $eventScores[$eventType];
#});
# return $scores->sum();
#}
</code></pre>
<p>比起以前用switch,现在用数组找映射关系,使得代码更简洁了.但是现在有一个问题,switch的default给漏了,因此,当要使用数组找关系时,我们要判断事件类型是否在数组中.</p>
<pre><code> # function githubScore($username)
#{
// ...
#$scores = $eventTypes->map(function ($eventType) {
#$eventScores = [
# 'PushEvent' => 5,
# 'CreateEvent' => 4,
# 'IssuesEvent' => 3,
# 'CommitCommentEvent' => 2,
#];
if (! isset($eventScores[$eventType])) {
return 1;
}
# return $eventScores[$eventType];
# });
# return $scores->sum();
# }
</code></pre>
<p>额,现在看起来,好像并不比switch好到哪儿去,不用担心,希望就在前方.</p>
<h3>Associative Collections(关联数组集合)</h3>
<blockquote><p>Everything is better as a collection, remember?</p></blockquote>
<p>到目前为止,我们用的集合都是索引数组,但是collection也给我们提供了处理关联数组强大的api.</p>
<p>你以前听过"Tell, Don't Ask"原则吗?其主旨就是你要避免询问一个对象关于其自身的问题,以便对你将要处理的对象做出另一个决定.相反,相反,你应该把这个责任推到这个对象上,所以你可以告诉它需要什么,而不是问它问题.</p>
<p>那说到底,这个原则跟咱们例子有什么关系呢?我很happy你能这么问,ok,让我们再看下那个if判断.</p>
<pre><code> # $eventScores = [
# 'PushEvent' => 5,
# 'CreateEvent' => 4,
# 'IssuesEvent' => 3,
# 'CommitCommentEvent' => 2,
#];
if (! isset($eventScores[$eventType])) {
return 1;
}
# return $eventScores[$eventType];</code></pre>
<p>嗯,我们现在呢就是在问这个关联数组是否存在某个值,存在会怎么样..,不存在怎么样..都有相应的处理.</p>
<p>Collection通过get方法让"Tell, Don't Ask"这个原则变得容易实现,get()有两个参数,第一个参数代表你要找的key,第二个参数是当找不到key时,会返回一个默认值的设置.</p>
<p>如果我们把$eventScores变成一个Collection,我们可以把以前的代码重构成这样:</p>
<pre><code> $eventScores = collect([
'PushEvent' => 5,
'CreateEvent' => 4,
'IssuesEvent' => 3,
'CommitCommentEvent' => 2,
]);
return $eventScores->get($eventType, 1);</code></pre>
<p>ok,把这部分还原到总代码中:</p>
<pre><code> function githubScore($username)
{
$url = "https://api.github.com/users/{$username}/events";
$events = collect(json_decode(file_get_contents($url), true));
$eventTypes = $events->pluck('type');
$scores = $eventTypes->map(function ($eventType) {
return collect([
'PushEvent' => 5,
'CreateEvent' => 4,
'IssuesEvent' => 3,
'CommitCommentEvent' => 2,
])->get($eventType, 1);
});
return $scores->sum();
</code></pre>
<p>ok,我们所有处理简炼成" a single pipeline".(单一管道)</p>
<pre><code> function githubScore($username)
{
$url = "https://api.github.com/users/{$username}/events";
$events = collect(json_decode(file_get_contents($url), true));
return $events->pluck('type')->map(function ($eventType) {
return collect([
'PushEvent' => 5,
'CreateEvent' => 4,
'IssuesEvent' => 3,
'CommitCommentEvent' => 2,
])->get($eventType, 1);
})->sum();
}
</code></pre>
<h3>Extracting Helper Functions(提取帮助函数)</h3>
<p>有的时候,map()函数体内容会占很多行,比如上例中通过事件找成绩这块逻辑.</p>
<p>虽然到现在为止,我们谈的也比较少,这只是因为我们使用Collection PipeLine(集合管道)但是并不意味这我们不用其他编程技巧,比如我们可以把一些小逻辑写道函数中封装起来.</p>
<p>比如,在本例中,我想把API调用和事件成绩查询放到独立的函数中,代码如下:</p>
<pre><code> function githubScore($username)
{
return fetchEvents($username)->pluck('type')->map(function ($eventType) {
return lookupEventScore($eventType);
})->sum();
}
function fetchEvents($username) {
$url = "https://api.github.com/users/{$username}/events";
return collect(json_decode(file_get_contents($url), true));
}
function lookupEventScore($eventType) {
return collect([
'PushEvent' => 5,
'CreateEvent' => 4,
'IssuesEvent' => 3,
'CommitCommentEvent' => 2,
])->get($eventType, 1);
}</code></pre>
<h3>Encapsulating in a Class (封装到一个类)</h3>
<p>现代PHPweb应用要获取某人GitHub成绩的典型做法是什么呢?我们肯定不是用一个全局函数来回互相调,对吧? 我们一般会定义一个带有namespace的类,方法的"封装型"自己定,</p>
<pre><code> class GitHubScore
{
public static function forUser($username) {
return self::fetchEvents($username)
->pluck('type')
->map(function ($eventType) {
return self::lookupScore($eventType); })->sum();
}
private static function fetchEvents($username) {
$url = "https://api.github.com/users/{$this->username}/events";
return collect(json_decode(file_get_contents($url), true));
}
private static function lookupScore($eventType) {
return collect([
'PushEvent' => 5,
'CreateEvent' => 4,
'IssuesEvent' => 3,
'CommitCommentEvent' => 2,
])->get($eventType, 1);
}
</code></pre>
<p>有了这个类,GitHubScore::forUser('adamwathan') 即可获得成绩.<br>这种方法的一个问题是,由于我们不使用实际的对象,我们无法跟踪任何状态。 相反,你最终在一些地方传递相同的参数,因为你真的没有任何地方可以存储该数据</p>
<p>这个例子现在看起来没什么问题,但是你可以看到我们必须传$username给fetchEvents()否则它不知道要获取的是那个用户的huod信息.</p>
<pre><code> class GitHubScore {
public static function forUser($username)
{
return self::fetchEvents($username)
->pluck('type')
->map(function ($eventType) {
return self::lookupScore($event['type']); })
->sum();
}
private static function fetchEvents($username)
{
$url = "https://api.github.com/users/{$this->username}/events";
return collect(json_decode(file_get_contents($url), true)); }
// ...
}
</code></pre>
<blockquote><p>This can get ugly pretty fast when you've extracted a handful of small methods that need access to the same data.</p></blockquote>
<p>像本例这种情况,我一般会创建一个私有属性.<br>代替掉类中的静态方法,我在第一个静态方法中创建了一个实例,委派所有的任务给这个实例.</p>
<pre><code> class GitHubScore
{
private $username;
private function __construct($username)
{
$this->username = $username;
}
public static function forUser($username)
{
return (new self($username))->score();
}
private function score()
{
$this->events()
->pluck('type')
->map(function ($eventType) {
return $this->lookupScore($eventType);
})->sum();
}
private function events()
{
$url = "https://api.github.com/users/{$this->username}/events";
return collect(json_decode(file_get_contents($url), true));
}
private function lookupScore($eventType)
{
return collect([
'PushEvent' => 5,
'CreateEvent' => 4,
'IssuesEvent' => 3,
'CommitCommentEvent' => 2,
])->get($eventType, 1);
}
}
</code></pre>
<p>现在你得到了方便的静态API,但是其内部使用的对象是有它的状态信息.可以使你的方法署名可以更简短,非常灵巧!</p>
<blockquote><p>额,真不容易,从晚上9点干到凌晨3:30,虽然辛苦,但是又巩固了一遍,还是值得的.2017/04/16 03:34</p></blockquote>
<p>由于时间有限,未能复查,翻译的不周到的地方,麻烦你留言指出,我再改正,谢谢!</p>
Decorator Pattern With Laravel 装饰者模式
https://segmentfault.com/a/1190000008996458
2017-04-09T02:06:22+08:00
2017-04-09T02:06:22+08:00
lcoding
https://segmentfault.com/u/qzuser_58592a1da62d9
2
<h2>Decorator Pattern 装饰者模式</h2>
<p><strong>纲要:</strong></p>
<pre><code> 1. 一个初学者的疑惑
2. 装饰者模式的特点
3. 简单case掌握装饰者模式
4. laravel中装饰者模式的应用
</code></pre>
<p><strong>Confusing:</strong><br> 刚开始研究laravel源码之前,对于"装饰者模式"我也是知之甚少,而对于“装饰者模式”的学习起因于创建一个中间件的时候,我始终都不太明白,中间件中的$next闭包是怎么传进来,因此好奇心强的我google了大量的前辈的博客和文章,学到了很多以前不知道的知识点,才明白中间件加载的原理,使我受益匪浅,在此非常感谢这些前辈的无私奉献.ok,回到正题.</p>
<p><img src="/img/bVLUnw?w=1530&h=739" alt="clipboard.png" title="clipboard.png"></p>
<p><strong>装饰者模式的特点</strong><br><a href="https://link.segmentfault.com/?enc=rw6qQJC%2B8kO11JXgQCgiAg%3D%3D.uV8p5%2B9ueoi37O8aMFmEFIEnD4%2Fv7pibqhsXFNhoy7VyHFcJna9tPCRgQlyx6y%2FsSy59gTMxWnMWw1znGRSZ5fwvniV0yLPOSTIzA%2FTvSnYB%2FRdd8rxK00mXTiPYT65EV6Bc7CQWgZHINwljX99NiS0Th1a2SRXgFdMWTBXaVeBBwRcCHylBlZJhfMJbC9lzD3DBf6S0eiZ6bDCvoqobWT4X8Isr6mcePB1bYamfvKM%3D" rel="nofollow">详细介绍点我</a></p>
<p>“(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。<br>(2) 装饰对象包含一个真实对象的引用(reference)<br>(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。<br>(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。” --------------引自<百度百科></p>
<p><strong>简单case掌握装饰者模式</strong><br>注意:若是以前没接触过该模式,建议先看下这篇文章<a href="https://segmentfault.com/a/1190000008996316">《laravel 框架关键技术解析》学习笔记之装饰者模式</a>,再回头看本文效果更佳.<br>好吧,如果是第一次看装饰者模式,看上面特性"官方阐述",确实有点不知所云,ok,为方便大家能快速理解该模式,我写了一个"laravel源码简化版"的case,放代码之前给大家说下实现该模式最核心的东西,就靠两个方法,如下:</p>
<ol>
<li><p>call_user_func() <a href="https://link.segmentfault.com/?enc=GsN9izW3G56fcJtwEVacQQ%3D%3D.nFZgcS%2BjCKTbI5nSmUy9ZtzyiGFxkFLKMzgK8l8DdS3NleZrLRZGIqowhmr2%2BlxF2hhPv9IrlTnz4zhMA%2BNWaA%3D%3D" rel="nofollow">不懂就点我</a></p></li>
<li><p>array_reduce() <a href="https://link.segmentfault.com/?enc=bcp1cFvOQqFxDSS9RvO8vg%3D%3D.KvG7kPK0tilp81rsTs6g293wfAQBSPBadsekhgYa5B%2F%2BgS1hloqiaR8mpmZJN%2F8VBlXKzlCcbjs0iHfeQVU%2Fww%3D%3D" rel="nofollow">你应该看看我</a></p></li>
</ol>
<p>代码:</p>
<pre><code>
interface func{
public static function handle($next);
}
class beauty implements func{
public static function say($next){
$next();
echo "我看不上屌丝";
}
}
class guy implements func{
public static function say($next){
echo '从前有个程序员想找个女朋友.';
$next();
}
}
$e=[guy::class,beauty::class];
function getClosure(){
return function ($a,$b){
return function()use($a,$b){
$b::say($a);
};
};
}
$d = function(){ echo '找了许久,仍未任何起色,突然有一天,碰见心仪的女神,结果,女神说:'."\n";};
call_user_func(array_reduce($e,getClosure(),$d));
</code></pre>
<pre><code> 先别着急看代码,先让代码运行下,看看结果是什么怎么样的?然后再去分析代码,
效果会好一点.ok,为了照顾新手朋友,我把执行过程,给大家梳理一下吧.
</code></pre>
<p><strong>执行流程:</strong><br>第一步.<br>首先getClosure()函数调用返回的是一个闭包:</p>
<pre><code>function($a,$b){
return function()use($a,$b){
return $b::say($a);
}
}
</code></pre>
<p>第二步.<br>将$d和$e[0]作为第一步返回结果的参数并且执行,返回结果:</p>
<pre><code>function()use($a,$b){
#此时$b="guy"
#$a = $d=function(){echo '找了许久,仍未任何起色,突然有一天,碰见心仪的女神,结果,女神说:'."\n";}
return $b::say($a);
}
</code></pre>
<p>第三步.<br>将第二部的结果和$e[1]作为第一步返回结果的参数并且执行,返回结果:</p>
<pre><code> function()use($a,$b){
#此时$b="beauty" $a='第二步骤返回结果'
# $a = function()use($,'beacuty'){
# beauty::say(function()use('guy',$d){
# return guy::say($d);
# })
# }
return $b::say($a);
}
</code></pre>
<p>第四步.执行call_user_func()调用array_reduce()返回的闭包即第三步的结果.<br>beauty::say($d) ==>$d()=>echo '从前有个程序员想找个女朋友.'; =>echo "'找了许久,仍未任何起色,突然有一天,碰见心仪的女神,结果,女神说:"=>"我看不上屌丝"</p>
<p>执行过程类似如图(仔细体会,好似洋葱一样,从最外层渗透进去到最内层,再从最内层到最外层):<br><img src="/img/bVLUvR?w=1152&h=648" alt="图片描述" title="图片描述"></p>
<p><strong>laravel中装饰者模式的应用</strong><br>这里给出laravel框架的源码进行对比分析.</p>
<pre><code>文件index.php line 50
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
文件Illuminate\Foundation\Http\Kernel.php
public function handle($request)
{
try {
$request->enableHttpMethodParameterOverride();
$response = $this->sendRequestThroughRouter($request);
} catch (Exception $e) {
....省略异常处理
}
$this->app['events']->fire('kernel.handled', [$request, $response]);
return $response;
}
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request');
$this->bootstrap();
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
protected function dispatchToRouter()
{
return function ($request) {
$this->app->instance('request', $request);
return $this->router->dispatch($request);
};
}
文件Illuminate\PipeLine\PipeLine.php
public function send($passable)
{
$this->passable = $passable;
return $this;
}
public function through($pipes)
{
$this->pipes = is_array($pipes) ? $pipes : func_get_args();
return $this;
}
public function then(Closure $destination)
{
$firstSlice = $this->getInitialSlice($destination);
$pipes = array_reverse($this->pipes);
return call_user_func(
array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable
);
}
protected function getInitialSlice(Closure $destination)
{
return function ($passable) use ($destination) {
return call_user_func($destination, $passable);
};
}
protected function getSlice()
{
return function ($stack, $pipe) {
return function ($passable) use ($stack, $pipe) {
// If the pipe is an instance of a Closure, we will just call it directly but
// otherwise we'll resolve the pipes out of the container and call it with
// the appropriate method and arguments, returning the results back out.
if ($pipe instanceof Closure) {
return call_user_func($pipe, $passable, $stack);
} else {
list($name, $parameters) = $this->parsePipeString($pipe);
return call_user_func_array([$this->container->make($name), $this->method],
array_merge([$passable, $stack], $parameters));
}
};
};
}
文件Illuminate\Foundation\Application.php
public function shouldSkipMiddleware()
{
return $this->bound('middleware.disable') &&
$this->make('middleware.disable') === true;
}
</code></pre>
《laravel 框架关键技术解析》学习笔记之装饰者模式
https://segmentfault.com/a/1190000008996316
2017-04-09T01:00:27+08:00
2017-04-09T01:00:27+08:00
lcoding
https://segmentfault.com/u/qzuser_58592a1da62d9
1
<p><strong>装饰者模式</strong>:<br>是在开放-关闭原则下实现动态添加或减少功能,提高程序的扩展性.<br><a href="https://link.segmentfault.com/?enc=leC7sObXoBJlZW5UZ4kbYQ%3D%3D.ltUg7vEftN8esDwBDhTdAa1nTcJuuDPexAQSgqEqWueNzi6HhV85wBEYnYxgtR8rYbthmrA985g8jrzB1%2BFNNyqyLGon9b9%2F%2FiyPJWF8MAjftDb0llk5qkmmiabnFjETSFxNh8yOIDMUZVGFcytYCuQA4SwoHyKzCrGTK1YCyyFM8X%2BijxPB1U0xmkA9jl%2Fv%2BPn82hQToPuBLrORMWIBf6bTzLA48EjPNR7pY84hJRk%3D" rel="nofollow">详细介绍</a><br>注:</p>
<ol>
<li><p>本文可以作为学习"装饰者模式"的基础篇,但是我个人更建议配套<a href="https://segmentfault.com/a/1190000008996458">Decorator Pattern With Laravel 装饰者模式</a>来学习效果更佳.</p></li>
<li><p>本文中的例子是由《laravel 框架关键技术解析》中摘抄的。有兴趣的朋友可以自行购买(这本书不能说写的多棒,但是作者写的很用心,laravel关键部分源码讲的很细,学习源码很有帮助)</p></li>
</ol>
<pre><code><?php
interface Decorater{
public function display();
}
class XiaoFang implements Decorater{
private $name;
public function __construct($name){
$this->name = $name;
}
public function display(){
echo "我是".$this->name."我出门了!!!".'<br/>';
}
}
class Finery implements Decorater{
private $component;
public function __construct(Decorater $component){
$this->component = $component;
}
public function display(){
$this->component->display();
}
}
class Shoes extends Finery{
public function display(){
echo '穿上鞋子'.'<br/>';
parent::display();
}
}
class Skirt extends Finery{
public function display(){
echo '穿上裙子'.'<br/>';
parent::display();
}
}
class Fire extends Finery{
public function display(){
echo '出门前先整理头发'.'<br>';
parent::display();
echo '出门后再整理一下头发'.'<br>';
}
}
$xiaofang = new XiaoFang('小芳');
$shoes = new Shoes($xiaofang);
$skirt = new Skirt($shoes);
$fire = new Fire($skirt);
$fire->display();</code></pre>
<p>运行下看看结果,理解起来会更佳,执行流程不复杂就不赘述了:</p>
<p>出门前先整理头发 <br>穿上裙子 <br>穿上鞋子<br> 我是小芳我出门了!!! <br>出门后再整理一下头发</p>
Win10搭建Laravel-HomeStead
https://segmentfault.com/a/1190000008984711
2017-04-08T02:48:21+08:00
2017-04-08T02:48:21+08:00
lcoding
https://segmentfault.com/u/qzuser_58592a1da62d9
0
<p><strong>一.介绍</strong><br>简单说下Homestead吧,它有两个功能:</p>
<ol>
<li><p>就是为了能够在本地方便开发laravel应用,体现laravel开发的优雅性.(但是前期安装的时候因为各种坑,还是比较难受的)</p></li>
<li><p>使得开发环境和生产环境保持一致。Homestead就是web开发所需软件的一个集成包(.box结尾的文件),包括操作系统(ubuntu),php,mysql,nginx,hhvm,redis,memcache等.根据文件后缀,大家可以猜到这是一个类似"盒子"的东西,其实就是装了"web开发软件"的盒子.ok,Let's start.</p></li>
</ol>
<p><strong>二.安装环境+Requirements:</strong></p>
<ul>
<li><p>开发环境:Win10</p></li>
<li><p>Requirements: 1.Virtualbox(虚拟机) 2.vagrant 3.Git</p></li>
<li>
<p><strong>注意事项</strong>:</p>
<pre><code>1.win10默认会开启"Hyper-v"服务,得要**关闭**这个服务.
</code></pre>
</li>
</ul>
<p><img src="/img/bVLRnL?w=1183&h=800" alt="图片描述" title="图片描述"></p>
<pre><code>2.virtualbox下载的时候需要注意一下版本,
有的版本下完以后连适配器都没有,各种各样的问题都有,所以这块我建议大家跟我下一样的吧.</code></pre>
<p><strong>下载链接:</strong><a href="https://link.segmentfault.com/?enc=OV5Alu3Z8clTB1URkeiqsQ%3D%3D.gbxcxmX%2Fdnn7zw69hT%2FBZkpnSvMuQLV18xZLuxY%2BykCLlLVEd%2BO%2FhfQYW%2BKXhIX15lO5u1OITWBwMZG1ZrjZWJyH4N%2F4BkjkBOvY%2Bjs06fJ0HYz6SYaFIzR8ZHQWqcQi" rel="nofollow">VirtualBox-5.0.10-104061-Win.exe</a><br><img src="/img/bVLRoD?w=1200&h=257" alt="图片描述" title="图片描述"></p>
<pre><code>3.
vagrant官网:https://www.vagrantup.com/ //下载最新版本即可
git官网:https://git-scm.com/downloads
</code></pre>
<p><strong>三.安装完毕后注意事项</strong></p>
<ul>
<li><p>以上所用到的软件安装这块就不细讲了,都是"傻瓜化"操作.</p></li>
<li>
<p>接下来需要配置一下git的ssh密钥,因为我们要用git从github上把Homestead"clone"下来(注意此Homestead非彼Homestead,后面会看到区别,这块先不解释)<br>操作:任意目录,鼠标右键,选择"git bash",执行下面代码</p>
<pre><code>ssh-keygen -t rsa -C "you@homestead" </code></pre>
</li>
<li>
<p>从github克隆Homestead,执行:</p>
<pre><code> git clone https://github.com/laravel/homestead.git Homestead</code></pre>
</li>
<li>
<p>上述命令执行完后,会在当前用户的家目录下创建Homestead目录,我们可以执行以下命令进到家目录:</p>
<pre><code>cd ~ </code></pre>
<p>注释://查看家目录,pwd 可以从进入相应的文件夹查看目录结构,执行:</p>
<pre><code> init.bash //会生成Homestead.yaml文件</code></pre>
</li>
<li><p>cd Homestead/ <br> 用编辑器打开Homestead.yaml,做如图配置<br><strong>注意:用“空格”,不要用"Tab"键对齐字段,否则Ruby会解析错误</strong><br><img src="/img/bVLRrX?w=1077&h=725" alt="图片描述" title="图片描述"></p></li>
</ul>
<p><strong>四.启动前注意事项</strong></p>
<ol><li><p>virtualbox,网线配置:<br>要勾选"接入网线"</p></li></ol>
<p><img src="/img/bVLRr2?w=997&h=606" alt="图片描述" title="图片描述"></p>
<p>2.<strong>如果虚拟机连接方式用了"网络地址转换NAT",得确保本地ip地址和Homestead.yaml中配置的ip地址的网段是一致的如图</strong>:<br><img src="/img/bVLRsy?w=1007&h=848" alt="图片描述" title="图片描述"></p>
<p>3.那么问题来了,<strong>如果两个网段不一样呢</strong>,<br>比如上例中的ipv4地址为10.xx.xx.x,而Homestead.yaml中ip还是192.168.x.x<br>这是该怎么解决呢?<br>答:可以这样处理<br>第一步:virtualbox<strong>选择'仅主机'</strong>连接方式(所有的虚拟系统是可以相互通信的,但虚拟系统和真实的网络是被隔离开的);<br>详细介绍:<a href="https://link.segmentfault.com/?enc=rWYfPGIuzelWRQgcRZFAbQ%3D%3D.Rwqvr347OF9OgA%2BQvTRJV%2Bhck0PpLoY3ZlGt7lHmEmD6Eu2MKn9Mh76sVw7ZtERZeYaQjfq4APiPkB5wUUUr2w%3D%3D" rel="nofollow">虚拟机连接方式</a><br><img src="/img/bVLRsJ?w=983&h=536" alt="图片描述" title="图片描述"></p>
<p>第二步:配置ip<br><img src="/img/bVLRs5?w=1162&h=472" alt="图片描述" title="图片描述"></p>
<p><img src="/img/bVLRtj?w=1189&h=882" alt="图片描述" title="图片描述"></p>
<p>之后保存即可。</p>
<p><strong>五.启动</strong></p>
<ol>
<li>
<p>首先执行(GIT):</p>
<pre><code>vagrant box add laravel/homestead //任意目录下都可以
应该会自动从镜像地址下载Homestead,可以将地址复制出来以后用迅雷下载,我下载了用了一个小时左右就好了(hc-download文件)。
当然这步也可以用离线安装.
首先,将hc-download文件改名为:homestead.box
执行: vagrant box add laravel/homestead D:/homestead/homestead.box
百度网盘链接: https://pan.baidu.com/s/1bp079cb 密码: rjpe</code></pre>
</li>
<li>
<p>启动,执行:</p>
<pre><code> cd ~/Homestead
vagrant up </code></pre>
<p>效果如图:<img src="/img/bVLRtQ?w=886&h=874" alt="图片描述" title="图片描述"></p>
</li>
</ol>
<p><strong>六.vagrant 常用命令</strong></p>
<pre><code> vagrant --help
vagrant up
vagrant halt //优雅关闭Homestead
vagrant box list
vagrant box remove '盒子名称' //从vagrant中去除添加的盒子
vagrant --reload //重新加载配置
.....</code></pre>