让 Laravel 完美支持 Memcache 及使用 zizaco/entrust 权限认证

今天想试着体验一下laravel下的rbac,
于是就找到了zizaco/entrust
由于我使用的是windows环境,根本用不了memcached,而entrust必须使用非file/database形式的缓存
所以我决定使用memcache来替代,以下是下解决办法。

Memcache完善部分

  1. 修改Vendor\Illuminate\Cache\CacheServiceProvider.php,添加以下代码:

        $this->app->singleton('memcache.connector', function () {
            return new MemcacheConnector;
        });
  2. 修改Vendor\Illuminate\Cache\CacheManager.php,添加以下代码:

        /**
         * Create an instance of the Memcache cache driver.
         *
         * @param  array  $config
         * @return \Illuminate\Cache\MemcachedStore
         */
        protected function createMemcacheDriver(array $config)
        {
            $prefix = $this->getPrefix($config);
    
            $memcache = $this->app['memcache.connector']->connect($config['servers']);
    
            return $this->repository(new MemcacheStore($memcache, $prefix));
        }
  3. 添加Vendor\Illuminate\Cache\MemcacheConnetcor.php,
    这是因为memcache->getVersion不同于memCache,只会返回一个版本字符串。

        <?php
    
        namespace Illuminate\Cache;
    
        use Memcache;
        use RuntimeException;
    
        class MemcacheConnector
        {
            /**
             * Create a new Memcache connection.
             *
             * @param  array  $servers
             * @return \Memcache
             *
             * @throws \RuntimeException
             */
            public function connect(array $servers)
            {
                $memcache = $this->getMemcache();
    
                // For each server in the array, we'll just extract the configuration and add
                // the server to the Memcache connection. Once we have added all of these
                // servers we'll verify the connection is successful and return it back.
                foreach ($servers as $server) {
                    $memcache->addServer(
                        $server['host'], $server['port'], true, $server['weight']
                    );
                }
    
                $memcachedStatus = $memcache->getVersion();
    
                if (!($memcachedStatus)) {
                    throw new RuntimeException('No Memcache servers added.');
                }
    
                return $memcache;
            }
    
            /**
             * Get a new Memcache instance.
             *
             * @return \Memcache
             */
            protected function getMemcache()
            {
                return new Memcache;
            }
        }
  4. 你还需要添加Vendor\Illuminate\Cache\MemcacheStore.php,这个文件是实现Cache的方法的,
    由于memcached的很多方法在memcache中是不存在的,所以你需要重写这部分代码。

        <?php
    
        namespace Illuminate\Cache;
    
        use Memcache;
        use Illuminate\Contracts\Cache\Store;
    
        class MemcachedStore extends TaggableStore implements Store
        {
            /**
             * The Memcached instance.
             *
             * @var \Memcache
             */
            protected $memcache;
    
            /**
             * A string that should be prepended to keys.
             *
             * @var string
             */
            protected $prefix;
    
            /**
             * Create a new Memcache store.
             *
             * @param  \Memcache  $memcache
             * @param  string      $prefix
             * @return void
             */
            public function __construct($memcache, $prefix = '')
            {
                $this->setPrefix($prefix);
                $this->memcache = $memcache;
            }
    
            /**
             * Retrieve an item from the cache by key.
             *
             * @param  string|array  $key
             * @return mixed
             */
            public function get($key)
            {
                $value = $this->memcache->get($this->prefix.$key);
    
                if ($this->memcache->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->memcache->getMulti($prefixedKeys, null, Memcached::GET_PRESERVE_ORDER);
    
                if ($this->memcache->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->memcache->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->put($this->prefix.$key, $value,  $minutes * 60);
                }
    
        //        $this->memcache->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->memcache->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->memcache->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->memcache->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->memcache->delete($this->prefix.$key);
            }
    
            /**
             * Remove all items from the cache.
             *
             * @return void
             */
            public function flush()
            {
                $this->memcache->flush();
            }
    
            /**
             * Get the underlying Memcached connection.
             *
             * @return \Memcached
             */
            public function getMemcache()
            {
                return $this->memcache;
            }
    
            /**
             * 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.':' : '';
            }
        }
  5. 修改Vendor\Illuminate\Cache\Repository.php,主要问题是使用memcache时,如果不存在返回的是false,而不是null,所以你需要修改使用!empty($value)来判定。为了最小化的影响代码,if($this->getStore() instanceof MemcacheStore)来判定当前的cache是Memcache.

        /**
         * Determine if an item exists in the cache.
         *
         * @param  string  $key
         * @return bool
         */
        public function has($key)
        {
            if($this->getStore() instanceof MemcacheStore)
            {
                return ! empty($this->get($key));
            }
            return ! is_null($this->get($key));
        }
        </code>
    
        <code>
        public function remember($key, $minutes, Closure $callback)
            {
                // If the item exists in the cache we will just return this immediately
                // otherwise we will execute the given Closure and cache the result
                // of that execution for the given number of minutes in storage.
    
                //add by liuxc for memcache.
                if($this->getStore() instanceof MemcacheStore)
                {
                    $value = $this->get($key);
                    if(!empty($value))
                        return $value;
                    $this->put($key, $value = $callback(), $minutes);
                    return $value;
                }
                if (! is_null($value = $this->get($key))) {
                    return $value;
                }
                $this->put($key, $value = $callback(), $minutes);
    
                return $value;
            }

至此laravel可以完美支持memcache了。接下来只要改少量代码就可以支持zizaco/entrust

zizaco/entrust修改部分

  1. 由于\vendor\zizaco\entrust\src\Traits\entrustUserTrait.php中调用了Config::get('cache.ttl')作为缓存时间,如下:

        public function cachedRoles()
        {
            $userPrimaryKey = $this->primaryKey;
            $cacheKey = 'entrust_roles_for_user_'.$this->$userPrimaryKey;
            return Cache::tags(Config::get('entrust.role_user_table'))->remember($cacheKey, Config::get('cache.ttl'), function(){
                return $this->roles()->get();
    
            });
        }
  2. 所以你可能需要修改Config/Cache.php并添加以下代码

        //add for cache tag minutes;
            'ttl'   =>'60',
  3. 修改\vendor\laravel\framework\src\illuminate\Foundation\Auth\User.php
    **注意,在修改之后使用Auth::user->may()而不是用can()方法。

        use Zizaco\Entrust\Traits\EntrustUserTrait;
    
        class User extends Model implements
            AuthenticatableContract,
            AuthorizableContract,
            CanResetPasswordContract
        {
            use Authenticatable, Authorizable, CanResetPassword,EntrustUserTrait {
                EntrustUserTrait::can as may;
                Authorizable::can insteadof EntrustUserTrait;
            }
        }
  4. 修改\vendor\zizaco\entrust\src\Entrust\Entrust.php 中的can 方法,这样你才可能是blade模版中愉快地使用@permission了。

        /**
         * Check if the current user has a permission by its name
         *
         * @param string $permission Permission string.
         *
         * @return bool
         */
        public function can($permission, $requireAll = false)
        {
            if ($user = $this->user()) {
                return $user->may($permission, $requireAll);
            }
    
            return false;
        }
本帖已被设为精华帖!
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 2

哈哈,吓我一跳。。

7年前 评论

可以用 PHP-Casbin 啊,https://github.com/php-casbin/php-casbin
支持 Laravel 的扩展 Laravel-Authzhttps://github.com/php-casbin/laravel-auth...
支持 rbac、通过地址验证权限、restful、中间件、缓存redis Memcache

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!