yii2 给Yii 添加一个变量,Yii::$service,并像组件component那样可以添加单例配置

在yii2中,组件是可以通过配置的方式添加到Yii::$app中的。

现在我们想添加一个Yii静态变量,$service,下面都称呼这个变量为服务

可以通过Yii::$service访问,然后添加服务,以及服务的子服务,
譬如我添加了一个cms服务,以及cms服务的子服务article。

那么我想访问cms服务,可以通过Yii::$service->cms访问,

如果我想访问cms服务的子服务,那么,我可以通过Yii::$service->cms->article访问。

上面的使用访问和yii2的组件很类似,是单例模式。下面说具体实现方式:

1.创建一个Yii.php添加$service参数:  @vendor/fancyecommerce/fecshop/yii/Yii.php

<?php
$dir = __DIR__ . '/../../../yiisoft/yii2';
require($dir.'/BaseYii.php');

/**
 * Yii is a helper class serving common framework functionalities.
 *
 * It extends from [[\yii\BaseYii]] which provides the actual implementation.
 * By writing your own Yii class, you can customize some functionalities of [[\yii\BaseYii]].
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class Yii extends \yii\BaseYii
{
  public static $service;
  
}

spl_autoload_register(['Yii', 'autoload'], true, true);
Yii::$classMap = require($dir.'/classes.php');
Yii::$container = new yii\di\Container();

 

2.在入口文件,将对yii的Yii.php去掉,换成:

require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/yii/Yii.php');

也就是加载上面,我创建的文件。

然后修改index.php底部代码:

new fecshop\services\Application($config['services']);
unset($config['services']);
//var_dump($config);
$application = new yii\web\Application($config);
$application->run();

也就是添加了代码:

new fecshop\services\Application($config['services']);
unset($config['services']);

这个代码的作用是,给$service变量赋值,Yii::$service = fecshop\services\Application($config[‘services’]);

3.下面我们实现这个Application。

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\services;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class Application
{
  public $childService;
  public $_childService;
  
  
  public function __construct($config = [])
    {
        Yii::$service 		= $this;
        $this->childService = $config;
    }
  /**
   * 得到services 里面配置的子服务childService的实例
   */
  public function getChildService($childServiceName){
    if(!$this->_childService[$childServiceName]){
      $childService = $this->childService;
      if(isset($childService[$childServiceName])){
        $service = $childService[$childServiceName];
        $this->_childService[$childServiceName] = Yii::createObject($service);
      }else{
        throw new InvalidConfigException('Child Service ['.$childServiceName.'] is not find in '.get_called_class().', you must config it! ');
      }
    }
    return $this->_childService[$childServiceName];
  }
  
  /**
   * 
   */
  public function __get($attr){
    return $this->getChildService($attr);
    
  }
  
}

4. 在配置中添加配置:

[

'services' => [
    'cms' => [
        'class' => 'fecshop\services\Cms',
        
        # 子服务
        'childService' => [
            'article' => [
                'class'             => 'fecshop\services\cms\Article',
                'storage' => 'mysqldb', # mysqldb or mongodb.
            ],
        ],
    ],

]

5.上面的配置,给cms添加了一个子服务article。

cms 对应的class的代码如下:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\services;
use Yii;
use yii\base\InvalidValueException;
use yii\base\InvalidConfigException;
use fec\helpers\CSession;
use fec\helpers\CUrl;
/**
 * Breadcrumbs services
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class Cms extends Service
{
  /**
   * cms storage db, you can set value: mysqldb,mongodb.
   */
  public $storage = 'mysqldb';
  
}

artile的代码如下:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\services\cms;
use Yii;
use yii\base\InvalidValueException;
use yii\base\InvalidConfigException;
use fec\helpers\CSession;
use fec\helpers\CUrl;
use fecshop\services\Service;
use fecshop\services\cms\article\ArticleMysqldb;
use fecshop\services\cms\article\ArticleMongodb;
/**
 * Breadcrumbs services
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class Article extends Service
{
  public $storage = 'mongodb';
  protected $_article;
  
  
  public function init(){
    if($this->storage == 'mongodb'){
      $this->_article = new ArticleMongodb;
    }else if($this->storage == 'mysqldb'){
      $this->_article = new ArticleMysqldb;
    }
  }
  /**
   * Get Url by article's url key.
   */
  public function getUrlByPath($urlPath){
    //return Yii::$service->url->getHttpBaseUrl().'/'.$urlKey;
    return Yii::$service->url->getUrlByPath($urlPath);
  }
  /**
   * get artile's primary key.
   */
  public function getPrimaryKey(){
    return $this->_article->getPrimaryKey();
  }
  /**
   * get artile model by primary key.
   */
  public function getByPrimaryKey($primaryKey){
    return $this->_article->getByPrimaryKey($primaryKey);
  }
  
  
  
  /**
   * @property $filter|Array
   * get artile collection by $filter
   * example filter:
   * [
   * 		'numPerPage' 	=> 20,  	
   * 		'pageNum'		=> 1,
   * 		'orderBy'	=> ['_id' => SORT_DESC, 'sku' => SORT_ASC ],
   * 		'where'			=> [
   * 			'price' => [
   * 				'?gt' => 1,
   * 				'?lt' => 10,
   * 			],
   * 			'sku' => 'uk10001',
   * 		],
   * 	'asArray' => true,
   * ]
   */
  public function coll($filter=''){
    return $this->_article->coll($filter);
  }
  
  /**
   * @property $one|Array , save one data .
   * @property $originUrlKey|String , article origin url key.
   * save $data to cms model,then,add url rewrite info to system service urlrewrite.                 
   */
  public function save($one,$originUrlKey){
    return $this->_article->save($one,$originUrlKey);
  }
  
  public function remove($ids){
    return $this->_article->remove($ids);
  }
  
}

他们继承的service类代码如下:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\services;
use Yii;
use yii\base\Object;
use yii\base\InvalidConfigException;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class Service extends  Object
{
  public $childService;
  public $_childService;
  
  /**
   * 得到services 里面配置的子服务childService的实例
   */
  public function getChildService($childServiceName){
    if(!$this->_childService[$childServiceName]){
      $childService = $this->childService;
      if(isset($childService[$childServiceName])){
        $service = $childService[$childServiceName];
        $this->_childService[$childServiceName] = Yii::createObject($service);
      }else{
        throw new InvalidConfigException('Child Service ['.$childServiceName.'] is not find in '.get_called_class().', you must config it! ');
      }
    }
    return $this->_childService[$childServiceName];
  }
  
  /**
   * 
   */
  public function __get($attr){
    return $this->getChildService($attr);
    
  }
  
}

然后,我就可以使用了,按照上面的方法

Yii::$service->cms->$storage     # 获取存储方式,这个变量可以在service配置中注入。

Yii::$service->cms->article->remove($ids)  # cms服务对应子服务artile的删除功能。

 

由于这种方式是单例模式,因此,可以用这种方式,在controller和model之间,做一个中间服务层,方便日后的扩展和使用,譬如我如果把mysql换成了mongodb,只需要把这个服务的public方法 重新实现以下就可以了,目前fecshop采用了这种实现方式。

php 真正的多线程 pthread

对于php,有很多种多进程的实现,这里就不说了,下面介绍一种多线程的方式。 php真正的多线程实现方式,通过安装php的扩展:pthread 扩展

安装步骤如下:

下载地址是这个:

https://github.com/krakjoe/pthreads

但是这个下载的是 版本3 也就是php 7 才能用的

我们需要使用2版本

然后刷新的页面如下,拖到最底部:

下一页找到版本2的

下载下来,这个v2 才是php5才可以使用的

下载下来,安装:

或者,您直接这样下载:

cd /tools  
   wget https://github.com/krakjoe/pthreads/archive/v2.0.10.zip  
   unzip   v2.0.10.zip  
   cd pthreads-2.0.10  
   /usr/local/php/bin/phpize  
   ./configure --with-php-config=/usr/local/php/bin/php-config    
   make  
   make install

注意:您的php 在编译的时候需要开启 –enable-maintainer-zts

./configure --prefix=/usr/local/php --disable-fileinfo   --enable-fpm --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d --with-openssl --with-zlib --with-curl --enable-ftp --with-gd --with-xmlrpc  --with-jpeg-dir --with-png-dir --with-freetype-dir --enable-gd-native-ttf --enable-mbstring --with-mcrypt=/usr/local/libmcrypt --enable-zip --with-mysql=/usr/local/mysql --without-pear --enable-maintainer-zts 

 

vim /etc/php.ini 
添加
extension=pthreads.so

 

重启php  
/etc/init.d/php-fpm restart

 

我之前搞的一个用php多线程获取订单货运号的例子:

http://blog.csdn.net/terry_water/article/details/50273847

php 一个容易被混淆的概念,深入理解成员变量和 属性。

本文章有误,不要阅读该文章。

1.对于public 定义的类的变量称为成员变量,譬如:

public $age;

2.通过 get开头的方法定义的为属性,他们都可以通过$ob->age 访问,譬如:

public function getSex() {}  
public function setSex($sex) {}

3.下面我们看一下他们执行的优先级

class ob{  
  # 类成员变量
  public  $age =10;  
  
  # 属性方法
  public function getAge(){  
    return 18;  
  }  
    # 魔术方法 
  public function __get($name){  
    return 22 ;  
  }  
  
}

如果我执行:

$ob = new ob();  
$ob->age

执行的结果为:

1.返回的是public 定义的成员变量, 10

2.如果去掉public $age的定义,那么返回的是魔术方法   22

因为魔术方法对应的是,当某些成员变量不存在的时候,就会访问魔术方法__get

3.如果去掉 __get的方法的定义,才会访问对象的属性,那么返回的是 18

 

二:下面我们执行一点高级点的php知识:

有时候我们看似一个简单的赋值,可能在后面执行了很多我们看不到的东西,这个在yii2中有很多地方用到了。

class ob{  
    public $age = 10;  
    
  public function  setScore($score){  
    $this->age = 22;   
    }  
   
  public function  getScore(){  
    $this->age = 55;
    return 199;  
    }  
}

当我们执行:

$ob = new $ob()  
echo $ob->age;  # 结果输出10,这个没有问题,很容易理解
echo $ob->score;  # 结果为199
echo $ob->age;  # 结果输出55 
$ob->score = 10;  #并没有给score赋值,而是给成员变量age赋值22
echo $ob->age;  # 结果输出55

上面我写的代码有点离谱,不过对于理解这个概念很有帮助,可以看到我们利用封装的对象$ob,进行赋值,在当前代码中并没有赋值,而是干了其他的事情。

譬如在yii的model有一个赋值的函数

$model->attributes = […],这里执行的就是一个属性,也就是setAttributes,结果保存到了private类成员变量$_attributes中了。

 

 

 

yii2 多模板路径优先级加载view方式下- js和css 的解决

fecshop 使用了多模板view文件优先级加载,在view和layout文件比较容易解决

比较难弄的是js和css部分,用yii2提供的yii\web\AssetBundle; 做了很多尝试都没有解决,最后自己写了一个来最终实现多模板下   js和css,也根据模板路径优先级下载。

首先说一下多模板路径,下面有多个模板路径,按照优先级依次如下:

@appfront/theme/terry/theme01/

@fecshop/theme/base/front/

@fecshop/theme/base/base/

如果我要加载的view文件为   cms/index/index,  首先查看  @appfront/theme/terry/theme01/cms/index/index.php文件是否存在,如果不存在,则查看@fecshop/theme/base/front/cms/index/index.php,如果还不存在,则看文件@fecshop/theme/base/base/cms/index/index.php 是否存在,如果不存在则报错返回。

上面是多模板的思路,为了是解决view文件,在fecshop系统模板升级和用户二次开发模板的矛盾冲突。

现在我想要css和js也这样,通过优先级加载css和js,

下面是我的实现:定义组件配置  :

      
      'asset' => [
        'class' =>  'fecshop\services\page\Asset',
        # js config
        'jsOptions'	=> [
          # js config 1
          [
            'options' => [
              'position' =>  'POS_END',
            //	'condition'=> 'lt IE 9',
            ],
            'js'	=>[
              'js/jquery-3.0.0.min.js',
              'js/js.js',
            ],
          ],
          # js config 2
          [
            'options' => [
              'condition'=> 'lt IE 9',
            ],
            'js'	=>[
              'js/ie9js.js'
            ],
          ],
        ],
        # css config
        'cssOptions'	=> [
          # css config 1.
          [
            'css'	=>[
              'css/style.css',
              'css/ie.css',
            ],
          ],
          
          # css config 2.
          [
            'options' => [
              'condition'=> 'lt IE 9',
            ],
            'css'	=>[
              'css/ltie9.css',
            ],
            
          ],
          
        ],
        
        //'cssOptions' => [
        //	'condition'
        //],
        //'jsOptions' => [
        //	'position' =>  'POS_END',
        //]
        
      ],

2. 组件部分:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\services\page;
use Yii;

use yii\base\Component;
use yii\helpers\ArrayHelper;
use yii\helpers\Url;

/**
 * Breadcrumbs services
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 extends AssetBundle
 */
class Asset extends Component
{
  public $cssOptions;
  public $jsOptions; 
  /**
   * 在模板路径下的相对文件夹。
   * 譬如模板路径为@fecshop/app/theme/base/front
   * 那么js,css路径默认为@fecshop/app/theme/base/front/assets
   */
  public $defaultDir = 'assets';
    /**
   * 文件路径默认放到模板路径下面的assets里面
   */
  public function register($view){
    $assetArr = [];
    $themeDir = Yii::$app->page->theme->getThemeDirArr();
    if( is_array($themeDir) && !empty($themeDir)){
      if( is_array($this->jsOptions) && !empty($this->jsOptions)){
        foreach($this->jsOptions as $jsOption){
          if( isset($jsOption['js']) && is_array($jsOption['js']) && !empty($jsOption['js'])){
      
            foreach($jsOption['js'] as $jsPath){
              foreach($themeDir as $dir){
                $dir = $dir.'/'.$this->defaultDir.'/';
                $jsAbsoluteDir = $dir.$jsPath;
                if(file_exists($jsAbsoluteDir)){
                    $assetArr[$dir]['jsOptions'][] = [
                    'js' 		=>  $jsPath,
                    'options' 	=>  $this->initOptions($jsOption['options']),
                  ];
                  break;
                }
              }
            }
          }
        }	
      }
      
      if( is_array($this->cssOptions) && !empty($this->cssOptions)){
        foreach($this->cssOptions as $cssOption){
          if( isset($cssOption['css']) && is_array($cssOption['css']) && !empty($cssOption['css'])){
            foreach($cssOption['css'] as $cssPath){		
              foreach($themeDir as $dir){
                $dir = $dir.'/'.$this->defaultDir.'/';
                $cssAbsoluteDir = $dir.$cssPath;
                if(file_exists($cssAbsoluteDir)){
                  $assetArr[$dir]['cssOptions'][] = [
                    'css' 		=>  $cssPath,
                    'options' 	=>  $this->initOptions($cssOption['options']),
                  ];
                  break;
                }
              }
            }
          }
        }	
      }
    }
    if(!empty($assetArr)){
      foreach($assetArr as $fileDir=>$as){
        $cssConfig = $as['cssOptions'];
        $jsConfig = $as['jsOptions'];
        $publishDir = $view->assetManager->publish($fileDir);
        if(!empty($jsConfig) && is_array($jsConfig)){
          foreach($jsConfig as $c){
            $view->registerJsFile($publishDir[1].'/'.$c['js'],$c['options']);
          }
        }
        if(!empty($cssConfig) && is_array($cssConfig)){
          foreach($cssConfig as $c){
            $view->registerCssFile($publishDir[1].'/'.$c['css'],$c['options']);
          }
        }
      }
    }
  }
  
  
  public function initOptions($options){
    if(isset($options['position'])){
      if($options['position'] == 'POS_HEAD'){
        $options['position'] =  \yii\web\View::POS_HEAD;
      }else if($options['position'] == 'POS_END'){
        $options['position'] =  \yii\web\View::POS_END;
      }
    }
    return $options;
  }
}












上面的 Yii::$app->page->theme->getThemeDirArr() 表示得到多模板路径数组。

在layout文件中使用

\Yii::$app->page->asset->register($this);

 

经过测试,可以在多个模板路径的assets下面查找js,找到后返回,找不到,继续到下一个模板路径中找js或者css文件。最后发布。

 

 

Yii2 fecshop 电商开源项目

目前正在做一个开源电商系统,经过了将近一年的筹划,对框架的整体越来越清晰,目前正在框架搭建过程。github地址为:https://github.com/fancyecommerce

Fecshop入口部分:fancyecommerce/yii2_fecshop_app_advanced

Fecshop核心部分:fancyecommerce/yii2_fecshop

Fecshop后台框架部分:fancyecommerce/yii2_fec_adminfancyecommerce/yii2_fec_admin

Fecshop基础部分:fancyecommerce/yii2-fec

Yii2 fecshop 电商开源项目

完全安装:
安装入口扩展:fancyecommerce/yii2_fecshop_app_advanced,其他的扩展将以包依赖的方式自动安装。

项目已经开始,预计到2017年元旦出来第一个正式版本。

经过一年的筹划,将magento的灵活强大性和yii2的可配置性结合起来,尝试多种方式,构思底层架构,目前架构层面已经搭建好,现在开始代码填写,也就是本项目:FecShop。

架构特色:

1.解决三者之间的矛盾: a) fecshop系统核心代码,模板,数据库升级(系统文件升级) , b)第三方代码,模板,数据升级 (通过composer做fecshop的扩展), c)用户二次开发,代码,模板,数据修改(使用fecshop的用户,在fecshop的基础上做二次开发)

2.解决功能重构:加入功能服务层service,在模块View ,Controller与数据层model之间,加入功能服务层service,在架构层面可以很好的解决重构问题。
譬如产品模块,通过服务层对上层module透明,通过实现mysql 和mongodb的不同的服务层,即可实现功能重构。

结构组织:
fancyecommerce/yii2_fecshop_app_advanced,只是一个入口库包,提供appadmin(后台地 址),appfront(前端pc入口),apphtml5(前端移动设备入口),appserver(API入口),以及相应的初始化配置,此库包类似 yiisoft/yii_app_advenced,

fancyecommerce/yii2_fecshop为fecshop的核心代码部分,包括models层,组件服务层services,模块 层modules,模块层里面包括controller,block层,view层,以及view里面的layout,view文件,css,js等。用 户可以通过配置,或者多模板路径优先级的方式,在不更改系统文件的前提下,重写fecshop的功能。

对接口和事件概念的深究

1.对于接口,个人认为更多的解决依赖颠倒。

接口的作用,1:可以解耦,我个人认为更确切的是,接口可以让依赖关系颠倒,原来是a和b的关系(a依赖b),变成了a和c(a定义c),b和c的关系(b依赖c),其中c是接口,通过中间层c,间接让b依赖a,有点控制反转的味道。 2.接口,定义类的某些方法不能更改方法名和方法传入的参数。

工作顺序的颠倒: 两个人合作做一个功能,A和B分别做2个模块,A是消费方,B是生产方,原来的先后顺序为: B生产出来一个方法,告诉A调用这个方法,来消费B生产的方法,

变成了:A定义个接口C,然后A使用C接口里面的方法实现了自己的功能,然后把C接口扔给B,让B去实现这个接口。

由生产方做主导的工作顺序,转变成消费方做主导的工作顺序。

详细的请看:http://www.fancyecommerce.com/2016/07/01/%e5%af%b9%e6%8e%a5%e5%8f%a3%e7%9a%84%e8%a7%81%e8%a7%a3-yii2/

2. 关于事件:

事件给人感觉很啰嗦,下面举个例子:
在电商系统加入购物车功能中,定义
A .为加入购物车,把产品的信息写入到购物车表中。
A 完成了后,老板说,加入购物车功能中得加入一个log记录功能,然后程序猿在原来的文件中加入了
B 日志功能
后来,老板说,加入购物车扣库存,程序猿在原来的文件中加入了功能C,扣产品库存
后来,老板说,加入优惠券,程序猿在原来的文件中加入了D功能
a,B,C,D功能都在一个文件中,后期很难维护。

升级模式:

分为购物车模块A,log模块B,产品模块C,优惠券模块D
让BCD三个模块实现对应的方法,然后给予A,然后A 实现购物车模块的加入购物车

也就是说,A需要等BDC三个模块完成,然后A才能完成,也就是说A依赖于BCD

继续升级:

A做了购物车模块,然后抛出事件,然后执行事件,后续如果有BCD想要添加功能,直接在事件触发前绑定事件就可以
这样,这样通过事件的配置文件,实现了解耦,也就是A的功能不依赖于BCD来,A 只需要抛出事件,然后BCD绑定过去,然后A触发即可,
这样A不依赖于BCD了。A可以先完成工作,然后,BCD的事件函数做好后,绑定上就可以,
而且在使用购物车模块的时候,我有很多事件,如果我想使用B日志功能,我就绑定上去,如果不使用我就不绑定,
这样就可以在不同的场景下面,有不同的购物车功能。

在上面,我们可以看到,每次添加事件,对于A来说,还是要更该A里面的内容,绑定事件。
我们进行下面的思考:

我是一个电商的开发商,你是一个我的系统的使用者,还是老样子啊,我的库包你是不能改动的
但是你又想改动我的功能
我在我的库包中,做了一个加入购物车的功能,请问,你如何做到不改动我的文件的前提下,在加入购物车这个动作中,添加你的一个功能呢,譬如你想在加入购物车的时候,添加一个日志记录功能。

解决办法:还是依赖注入,依赖的是配置文件,在配置文件中加入事件,我在做加入购物车的功能的时候,就想到了肯定很多人想在这里加代码,哪里我就提前部署好了事件,
通过配置文件中的事件配置,来绑定事件执行。
如果你想在加入购物车这个动作中加入你的代码,你可以做一个事件,然后在配置中加入你写的事件配置就可以了,对吧?
你没有改动我的系统文件,你改动的是配置文件。

总结:
接口:是为了解决依赖关系,由A依赖B,变成了,A定义接口,让B依赖接口,间接实现B依赖A
事件:A依赖BCD,变成了A定义事件,让BCD依赖事件,间接实现了让BCD依赖A,
配置文件注入:通过配置文件注入到组件的初始化中,可以在配置文件中添加事件配置,间接实现了 更改系统功能,但是不改动系统文件内容。

yii2 关于helper类 ArrayHelper::merge()方法的介绍

yii\helpers\ArrayHelper::merge()

在web/index.php入口文件就可以看到这个方法:

譬如:

$config = yii\helpers\ArrayHelper::merge(
    # yii2  common config
  require(__DIR__ . '/../../common/config/main.php'),
    require(__DIR__ . '/../../common/config/main-local.php'),
    # yii config
  require(__DIR__ . '/../config/main.php'),
  require(__DIR__ . '/../config/main-local.php'),
  # yii fecshop config
  require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/config/fecshop.php'),
  # yii fecshop appfront config
  require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/app/appfront/config/appfront.php'),
  # third people  config
  
  # user second develop config.
  
  require(__DIR__ . '/../config/fecshop_local.php')
    

);
$config['homeUrl'] = $homeUrl;
$application = new yii\web\Application($config);

需要注意的是ArrayHelper::merge方法,合并后的数组结果是什么:

下面通过例子讲解:

$arr1 = [
  'name' 	=> 'terry',
  'age'	=>	15,
  'friend'=> [
    'zhangsan','lisi'
  ],
  'work' =>[
    'aa'	=> 11,
    'bb'	=> ['aa','bb'],
  ],

];


$arr2 = [
  'name' 	=> 'water',
  'age'	=>	22,
  'friend'=> [
    'zhangsan','wangwu'
  ],
  'work' =>[
    'cc'	=> ['77','bb'],
    'aa'	=> 22,
  ],

];


$arr3 = yii\helpers\ArrayHelper::merge($arr1,$arr2);

$arr3的值为:

array(4) { [“name”]=> string(5) “water” [“age”]=> int(22) [“friend”]=> array(4) { [0]=> string(8) “zhangsan” [1]=> string(4) “lisi” [2]=> string(8) “zhangsan” [3]=> string(6) “wangwu” } [“work”]=> array(3) { [“aa”]=> int(22) [“bb”]=> array(2) { [0]=> string(2) “aa” [1]=> string(2) “bb” } [“cc”]=> array(2) { [0]=> string(2) “77” [1]=> string(2) “bb” } } }

 

可以看到这个数组的作用:

1.如果key是数字,那么value就会进行数组合并,如果值相同,不会覆盖,会出现两个相同值的数组元素,譬如[1,2]和[2,3]通过merge函数合并的结果为[1,2,2,3].

2.如果数组的key不是数字,而且数组里面的value不是数字,则value会被覆盖掉

譬如[‘aa’ => ‘xxx’]和[‘aa’=>’yy’]  merge函数后的结果为[‘aa’=>’yy’],

3.如果数组的key不是数字,而value是数组(数组的key是数字),则会进行数组合并。

4.如果数组的key不是数字,而value是数组(数组的key不是数字),则value数组中的key回合第二个步骤那样处理。

 

具体可以自己琢磨,因此在使用这个方法的时候,要想key覆盖,key一定要用字符串,就和变量定义一样。

Terry对接口的见解 – php

A的行为 依赖于B的一些方法,但是B还没有开始做,所以,我们先通过接口实现定义各个空的方法,用来拿数据,大致的意思,我用一个比喻形容:A和B是革命党,A是 军方,需要用服装,B是生产方,必须等B生产好了,B告知A到哪里拿服装,哪里拿鞋子,A的服装队伍才能去拿衣服,也就是A依赖于B的信息,现在有了契 约,A直接告诉B衣服放到哪里,鞋子放到哪里,而且还有相应的生产尺码,A到时候去拿,等B生产好衣服鞋子的时候,按照契约C,直接把衣服和鞋子放到相应 的地方,对于A来说,有点皇帝的味道,到了时间,直接去拿衣服鞋子,另外一个比喻:税收就像接口,定义好了大家都要交税,国家就是定义接口的主体,原来是 农民B生产了粮食,A去拿粮食,A有点要饭的味道,因为A依赖于B,现在通过税收契约,直接规定B必须上交,不按照接口规定就办了你(报错退出),A有了 皇帝的味道。通过这个例子我们可以看到,原来是A依赖B,变成了,A定义C契约(接口),B依赖C,这样由A依赖B,间接实现了B依赖A。这就是接口的作 用。在产品设计方面可以颠倒主从依赖关系,有点控制反转的味道。

也就是让两个彼此影响的个人,通过接口,让其依赖相互颠倒

通过调用接口有什么好处呢?看似很啰嗦。我认为好处有如下几点: 1.在程序设计,尤其是产品,如果A依赖于B,A是产品的一部分,譬如用户组件(user component),B是用户自定义(譬如user,User extends ActiveRecord implements IdentityInterface),按照常理,需要B先行,然后A在行动,但是呢?在产品中,我们需要A先行动,B在行动,这就需要A行动的时候,告 诉B,你如何如何,这就是接口,告诉B要实现那些方法,A在B没有的情况下直接使用B的方法,然后强制B实现这些方法(通过接口),这样通过契约接口颠倒 了依赖,在程序实现上,完成了解耦, 也就是说,接口是使用方定义的,而不是提供方定义的,这个有点像客户让工厂加工某些东西,提供了图纸一样,如果B不按照图纸,A就不验收,就报错退出。 2.个人认为接口一旦存在,就是强制执行,除非法律更改(接口改变),在工作当中,为了扩展,经常的对某些方法进行修改,譬如 方法的参数增加了个数,由于php不想java那样,还可以限制传入的参数类型和返回的类型,所以php的接口限制要弱一些。不过还是比较有效,可以限制 开发者脑门一热,方法增加参数,或者把这个函数名改变了。

总结:接口的作用,1:可以解耦,我个人认为更确切的是,接口可以让依赖关系颠倒,原来是a和b的关系(a依赖b),变成了a和c(a定义c),b和c的关系(b依赖c),其中c是接口,通过中间层c,间接让b依赖a,有点控制反转的味道。 2.接口,定义类的某些方法不能更改方法名和方法传入的参数。

 

工作顺序的颠倒: 两个人合作做一个功能,A和B分别做2个模块,A是消费方,B是生产方,原来的先后顺序为: B生产出来一个方法,告诉A调用这个方法,来消费B生产的方法,

变成了:A定义个接口C,然后A使用C接口里面的方法实现了自己的功能,然后把C接口扔给B,让B去实现这个接口。

由生产方做主导的工作顺序,转变成消费方做主导的工作顺序。

 

另外:补充接口的另外的用途,可以做类型判断,譬如:

if ($identity instanceof IdentityInterface) { }

如果一个类继承了接口IdentityInterface,接可以使用instanceof 判断这个类是否实现了这个接口 ,进而可以在程序中做控制,譬如:

./yiisoft/yii2/web/User.php:202: if ($identity instanceof IdentityInterface) {