Php依赖注入容器Pimple的笔记

话说许久没有写技术类的笔记了,也该写点东西了,距离上次说 container 已经过去4个月了,期间也一直在学习,但是不知道写点什么好,说实话还是很怀念 14 年下半年,那半年的进步真的很大,那时候自己愿意看东西,也愿意写东西,也许写东西能让自己进步更快吧,所以17年了也应该继续进步了,否则问题大大的啊,下班的时候跟小伙伴聊天,做技术的就应该一直学习,否则就会被拉下很远很远。好了,说了这么多废话,说点正文吧,其实,想写 laravel 的 container 的笔记的,可是,自己看了许久,也没有很好的理解,理解到的部分也是很片面的东西,不过 laravel 的 container 真的很强大,要比今天写的这个强大的多,不过强大的带来的问题,就是可能会慢,毕竟用到了反射,今天这个 Pimple 则相对简单的多了,没有那么多复杂的东西,用起来也很顺手而且比较容易理解,所以今天就用这个做笔记了。

原本想分段做这个的笔记的,后来觉得应该吧知识点拆分开说明,然后再用整体代码在里面做注释的方式来解释这段更好一些,于是乎,准备开整,btw,过几天准备更新一下编辑器,现在这个自己搞得编辑器还是有些简陋的。好吧,进入正文了。

class Container implements \ArrayAccess

这个类实现了 ArrayAccess 接口,ArrayAccess 是个什么东西了,大家首先看一下 PHP 官网的手册 http://php.net/manual/zh/class.arrayaccess.php 好吧,我就简单说一句,就是让对象能够像数组一样操作了。具体需要实现的几个接口,大家看一下手册吧,我觉得我的说明肯定没有手册写得好,我更多理解的东西,会在代码注释中写一下。

public function __construct(array $values = array())
    {
        $this->factories = new \SplObjectStorage();
        $this->protected = new \SplObjectStorage();

        foreach ($values as $key => $value) {
            $this->offsetSet($key, $value);
        }
    }

在构造方法中又出现了 SplObjectStorage 类,那么这个 SplObjectStorage 是个什么东西呢,我们再去看一下手册 http://php.net/manual/zh/class.splobjectstorage.php 这个在手册中可惜翻译的不完全(根本没有翻译),所以我就简单的说说,这个类其实是实现了 Countable , Iterator , Serializable , ArrayAccess 这4个接口,这4个接口大家也可以看一下手册,看完了就知道这个类是干什么的了,我再来拆分说下 Countable 就是让一个类可以用一个计数器, Iterator 就是可以用 foreach 这些去循环,Serializable 就是可以序列化,ArrayAccess 上面说过了就不多说了。其实刚开始很难理解这个类,不过多看看手册,别人的示例代码,自己在多写一些可能就了解了。这个如何理解我也说不太好。大家见谅。

好吧,我觉得是额外知识点的就上面那些,等多的笔记我将在下面的代码注释中说明白,大家可以看看,顺便帮忙指正。

<?php

/*
 * This file is part of Pimple.
 *
 * Copyright (c) 2009 Fabien Potencier
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

namespace Pimple;

/**
 * Container main class.
 *
 * @author  Fabien Potencier
 */
class Container implements \ArrayAccess
{
    private $values = array(); // 存储 value 的数组
    private $factories; // 存储工厂方法的对象
    private $protected; // 存储保护方法的对象
    private $frozen = array(); // 存储冻结的数组,也就是在这个数组里面的 key 的 value 是不可更改的了
    private $raw = array(); // 存储
    private $keys = array(); // 存储 key 的数组

    /**
     * Instantiate the container.
     *
     * Objects and parameters can be passed as argument to the constructor.
     *
     * @param array $values The parameters or objects.
     */
    public function __construct(array $values = array())
    {
        // 构造工厂对象以及保护方法对象
        $this->factories = new \SplObjectStorage();
        $this->protected = new \SplObjectStorage();
        // 把初始化的值存放到现有的里面
        foreach ($values as $key => $value) {
            $this->offsetSet($key, $value);
        }
    }

    /**
     * Sets a parameter or an object.
     *
     * Objects must be defined as Closures.
     *
     * Allowing any PHP callable leads to difficult to debug problems
     * as function names (strings) are callable (creating a function with
     * the same name as an existing parameter would break your container).
     *
     * @param string $id    The unique identifier for the parameter or object
     * @param mixed  $value The value of the parameter or a closure to define an object
     *
     * @throws \RuntimeException Prevent override of a frozen service
     * 设置相关值以及对象
     */
    public function offsetSet($id, $value)
    {
        // 如果这个值被 frozen 了,就不允许更改了
        // 应该是为了保持高效性,会把 get 过的值存储起来,所以就不在调用了也就不允许更改了
        // 其实也可以更改,在下面的方法中说明
        if (isset($this->frozen[$id])) {
            throw new \RuntimeException(sprintf('Cannot override frozen service "%s".', $id));
        }
        // 存储值方法
        $this->values[$id] = $value;
        // 存储 key 的值
        $this->keys[$id] = true;
    }

    /**
     * Gets a parameter or an object.
     *
     * @param string $id The unique identifier for the parameter or object
     *
     * @return mixed The value of the parameter or an object
     *
     * @throws \InvalidArgumentException if the identifier is not defined
     * 获取值得方法
     */
    public function offsetGet($id)
    {
        // 如果没有设置 key 则抛出异常
        if (!isset($this->keys[$id])) {
            throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
        }

        // 如果 raw 里面已经有了, 或者 values 里面存储对应 key 的值不是个 obj,或者 protected 里面也有对应的值 或者值里面的方法存在,则直接返回,values 数组里面的结果
        if (
            isset($this->raw[$id])
            || !is_object($this->values[$id])
            || isset($this->protected[$this->values[$id]])
            || !method_exists($this->values[$id], '__invoke')
        ) {
            return $this->values[$id];
        }

        // 如果工厂方法里面设置了相关方法则要直接返回
        if (isset($this->factories[$this->values[$id]])) {
            return $this->values[$id]($this);
        }

        // 获取值里面的方法
        $raw = $this->values[$id];
        // 执行上面获取到的方法获取返回值 并且覆盖 values
        $val = $this->values[$id] = $raw($this);
        // 把原始方法存储到 raw 数组里面,用来给 raw 方法调用
        $this->raw[$id] = $raw;
        // 把这个值设置为冻结,不允许将来的更改
        $this->frozen[$id] = true;
        // 返回结果值
        return $val;
    }

    /**
     * Checks if a parameter or an object is set.
     *
     * @param string $id The unique identifier for the parameter or object
     *
     * @return bool
     * 获取 key 是否存在
     */
    public function offsetExists($id)
    {
        return isset($this->keys[$id]);
    }

    /**
     * Unsets a parameter or an object.
     *
     * @param string $id The unique identifier for the parameter or object
     * 删除掉 key
     */
    public function offsetUnset($id)
    {
        // 如果存在则删除相关的值
        if (isset($this->keys[$id])) {
            // 如果存储的是个对象,则删除相关的值
            if (is_object($this->values[$id])) {
                unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]);
            }
            // 删除普通数组里面的值
            unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]);
        }
    }

    /**
     * Marks a callable as being a factory service.
     *
     * @param callable $callable A service definition to be used as a factory
     *
     * @return callable The passed callable
     *
     * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object
     * 设置工厂方法
     */
    public function factory($callable)
    {
        if (!method_exists($callable, '__invoke')) {
            throw new \InvalidArgumentException('Service definition is not a Closure or invokable object.');
        }

        $this->factories->attach($callable);

        return $callable;
    }

    /**
     * Protects a callable from being interpreted as a service.
     *
     * This is useful when you want to store a callable as a parameter.
     *
     * @param callable $callable A callable to protect from being evaluated
     *
     * @return callable The passed callable
     *
     * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object
     */
    public function protect($callable)
    {
        if (!method_exists($callable, '__invoke')) {
            throw new \InvalidArgumentException('Callable is not a Closure or invokable object.');
        }

        $this->protected->attach($callable);

        return $callable;
    }

    /**
     * Gets a parameter or the closure defining an object.
     *
     * @param string $id The unique identifier for the parameter or object
     *
     * @return mixed The value of the parameter or the closure defining an object
     *
     * @throws \InvalidArgumentException if the identifier is not defined
     * 其实这个就是获取设置的对象或者方法
     */
    public function raw($id)
    {
        if (!isset($this->keys[$id])) {
            throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
        }
        if (isset($this->raw[$id])) {
            return $this->raw[$id];
        }

        return $this->values[$id];
    }

    /**
     * Extends an object definition.
     *
     * Useful when you want to extend an existing object definition,
     * without necessarily loading that object.
     *
     * @param string   $id       The unique identifier for the object
     * @param callable $callable A service definition to extend the original
     *
     * @return callable The wrapped callable
     *
     * @throws \InvalidArgumentException if the identifier is not defined or not a service definition
     * 修改已经存在的值
     */
    public function extend($id, $callable)
    {
        if (!isset($this->keys[$id])) {
            throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
        }

        if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) {
            throw new \InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id));
        }

        if (!is_object($callable) || !method_exists($callable, '__invoke')) {
            throw new \InvalidArgumentException('Extension service definition is not a Closure or invokable object.');
        }

        $factory = $this->values[$id];

        $extended = function ($c) use ($callable, $factory) {
            return $callable($factory($c), $c);
        };

        if (isset($this->factories[$factory])) {
            $this->factories->detach($factory);
            $this->factories->attach($extended);
        }

        return $this[$id] = $extended;
    }

    /**
     * Returns all defined value names.
     *
     * @return array An array of value names
     * 获取所有的 key
     */
    public function keys()
    {
        return array_keys($this->values);
    }

    /**
     * Registers a service provider.
     *
     * @param ServiceProviderInterface $provider A ServiceProviderInterface instance
     * @param array                    $values   An array of values that customizes the provider
     *
     * @return static
     * 注册自己的服务用的,后续文章体现
     */
    public function register(ServiceProviderInterface $provider, array $values = array())
    {
        $provider->register($this);

        foreach ($values as $key => $value) {
            $this[$key] = $value;
        }

        return $this;
    }
}

好吧,上面注释的都已经写出来了,个人觉得很简单,但是有几个方法我觉得可以扩展开来说,可能在过几天的文章里面展开来说说,基本上就是 register、extend和 raw 这几个方法的具体说明了。

哦对了,开始我觉得 factory 和 protect 方法差不多,其实还是有区别的,就是 factory 会有一个 $container 的参数,protect 方法就是存储的普通方法,区别就这么简单了。不过更多的方法,也会在后续文章说明的。

npm 以及 webpack 的一些笔记

先说点没用的,工作了3年了,相关东西也都接触一些,但是前端呢,还是按照以前的套路,做页面模版,写 js,写 css,但是现代相关的东西却没有深入的了解过。这不好啊,趁着现在有些空闲时间就想学习一下vue,但是 vue-cli 是使用 webpack 打包的,所以第一步就应该学习一下 webpack 趁机了解一下现代前端的开发思想。

本篇笔记总结自: http://www.jianshu.com/p/42e11515c10f,并针对笔记当日的最新版本对配置项进行响应的配置

安装 webpack

//全局安装
npm install -g webpack
//安装到你的项目目录
npm install --save-dev webpack

编译项目

webpack {entry file/入口文件} {destination for bundled file/存放bundle.js的地方}

不适用繁杂的命令,使用配置文件编译项目

module.exports = {
  entry:  __dirname + "/app/main.js",//已多次提及的唯一入口文件
  output: {
    path: __dirname + "/public",//打包后的文件存放的地方
    filename: "bundle.js"//打包后输出文件的文件名
  }
}

使用 node 的 package.json 设置 script 来执行编译

// 在package.json 的 scripts 里面添加下面的命令
"build": "webpack"
// 前面的是指在利用 npm run 后面执行的命令,问号后面的是真正执行的命令

使用webpack构建本地服务器

// 在 webpck.config.js 加入下面的配置文件
devServer: {
    contentBase: "./public",//本地服务器所加载的页面所在的目录
    // colors: true,//终端中输出结果为彩色 新版本貌似不需要这个了
    historyApiFallback: true,//不跳转
    inline: true//实时刷新
  } 

大概就这么多吧,Loader 和 Bable 没有总结,毕竟这两个扩展性比较强,需要用的时候,在查或者看文档就 ok 了,基本上 webpack 也就了解了一下了。

嘿呀,好久没有写东西了,很爽啊,正在逐步找回节奏,慢慢笔记会更好的。加油

过年小总结

图片alt

一转眼春节假期结束了,这个假期超级有意义啊,跟女朋友的亲事定下来了,也跟我来了北京了。剩下的事情就是攒钱了,毕竟要结婚买房了,攒钱计划也订好了,一定要按照计划执行清楚。

准备看代码的计划算是没执行,不过也看了一些还算可以接受。不过利用假期看了5本书,4本小说一本技术书。虽然技术书不多但是自己理解了一些没理解的东西以及如何更好的看书学习。看书果然不是看一次就行了,需要多多的看多多揣摩,等以后再看的时候专门看自己没有理解好的部分就可以了。

ok,又一篇流水账记录完成了。2017算是正式开始了,更加努力吧。就如同当初我保证的那样子,别急,给我一些时间啥都会有的。加油吧

从权限控制到合理分解系统的思考

本来这篇文章想写一个不利用轮子在laravel上实现rbac的东西的,其实也就是自己造一个rbac的轮子。

但是,最后自己还是懒了,不想写代码,所以呢,聊聊从权限管理让我思考的分解系统的一些东西吧,其实也不算是分解系统,仅仅就是想说说松耦合的事情。

曾经我以为我自己了解的不够多,但是通过这半年换工作以后我才意识到我了解的很多了,只不过有很多东西仅仅就是了解的程度而已,没有实践经验并没有什么卵用,而且了解的越多,不知道的就越多,就包括这次的权限控制这些东西,以前我总是认为这是个很高大上的东西,后面的学习我知道了rbac就是角色权限这些东西,现在我又认识到,这些东西应该是可以热插拔的可替换的,不应该对现有系统造成任何影响,也就是说我撤除掉权限相关的东西系统应该可以正常运行下去,还有,其实造一个轮子很难啊,因为可能需要我们为不同的框架去做相应的适配。

学习,永远都停不下来,我也不知道我上面说的东西对不对。就这样吧,看东西去了

对了,最重要的东西没说,说说思路。

首先我们应该有一个baseModel,所有的model对象都继承自baseModel,baseModel里面有hasRole,hasPermission,can ,cannot等这些方法用于权限的验证,然后blade那边我们也应该有相应的控制,至于角色权限相关,就按照自己的思路定义就可以了。

一些都应该是可替换的

再见2016

图片alt

2016马上就要结束了,风雨飘摇的一年,充满收获的一年。

刚刚过了年就跟前女友分手了,但是高兴的是立刻发现了更好的人。也许人无完人,我们在将来都有可能遇到更好的人。但是我想说的是,我很懒就坑你了,只要不放弃我,我愿意努力给你个美好的未来,我的口号还是以前那样,不求大富大贵腰缠万贯,起码衣食无忧。

正式毕业2年整,人更成熟了,更加平淡的看事情了,最终我还是回到了北京,算命的说我不能离家太远,最后我还是要回到哈尔滨。但是呢,我又一次出来了,我坚信自己的努力会得到相应的回报,只不过时间早晚的问题,多变的工作经历,让我有了更丰富的经历。都说2016是寒冬,但是在我看来,工作依然是给准备好的人的,在哈尔滨的时候就准备了几家面试。最终被pass了几家,我也pass了几家,最后挑选offer的时候没有选择58,而是选择了现在的公司,在面试的时候就聊了很多,大公司的利弊,小公司的利弊也都说的很清晰。而且自己也有一些方向,所以最后我更有倾向性的一家公司。说实话到现在也没有体会过大公司的感觉,小公司倒是工作了很多家,看看在有机会的吧,能够体会一下大公司的感觉,也许下次挑选公司就是以一个高级程序员或者更高的的身份直接杀向bat这样的公司了,近几年估计不会乱动,不过现在看来还是有难度的,小公司的好处就是什么都能接触到,但是由于小公司的限制不能够在某一领域深入进去,毕竟有更多的任务等待去解决,不过方向到现在我也没个目标,所以还是在小公司厮混。再有一个就是小公司的隐性福利必然没有大公司的完备,毕竟资金流有限。

做一个事情要坚持,坚持下去才会成功,知道的越多迷茫越多,方案就是不挺的学下去,在一个基础要牢靠,我就觉得我的基础有些差,现在也在不停的补充基础知识,还有就是知识让人分369等。既然出身不好,我就努力吧。

2017我还会在这个地方战斗下去,加油。学习,keep study。