月度归档:2014年04月

PHP包依赖管理工具Composer

Composer是一个管理PHP包依赖关系的工具。可以使用Composer方便地管理项目中一些第三方库和自己的库。官方地址:https://getcomposer.org。(国内镜像:http://pkg.phpcomposer.com/,具体配置参考这里的说明)

安装:(PHP必须是先安装的,并且它的路径必须在PATH中)

//方式1 不需要先进入目录(通过install-dir指定)
$curl -sS https://getcomposer.org/installer | php --install-dir=bin

//方式2 没有curl时
php -r "readfile('https://getcomposer.org/installer');" | php --install-dir=bin

稍做解释,https://getcomposer.org/installer实际是一个PHP脚本,通过管道让PHP来执行这个脚本,所以PHP是必须先安装的。这个脚本执行后就会在指定目录下生成一个叫composer.phar的文件,这个文件就是composer的主程序。

——文件composer.phar—————————————-
查看这个脚本开始部分:

#!/usr/bin/env php
......

我们平时在写一个shell脚本时,应该是:

#!/usr/bin/sh
......
##或者
#!/usr/bin/bash
......

意思是说,使用bash来解释当前这个脚本。那么env php应该就是要用php来解释当前这个脚本,为何要这个写法?实际上,PHP安装到系统上时,由于系统的差异,PHP这个解释器可能安装到了不同的目录,通过env php这种用法就可以去搜索PATH然后获取这个PHP解释器。那么总结来说就是可以直接执行composer.phar(调用PHP来解释这段脚本,所以PHP是必须安装并且可以找到的)。

可以把这个composer.phar移动到某个PATH目录下(比如/usr/bin/),这样就可以随意运行了(意思就是实际composer.phar放在什么地方并不重要,只要在输入命令是能找到对应命令即可):

mv composer.phar /usr/bin/composer

——文件composer.phar—————————————-

——Windows下安装——————————————–
#########################################
Windows下的安装包:
https://getcomposer.org/Composer-Setup.exe

安装过程会提示是否使用代理(http_proxy,注:只能是http代理),依赖PHP,PHP还需要启用相关的扩展(php_openssl等),安装过程会下载composer.phar,如果没有设置代理,可能非常缓慢。按照完成后会修改环境变量:
全局环境变量path:
C:\ProgramData\ComposerSetup\bin
这个路径下的composer是针对*inux的脚本,composer.bat是针对Windows下的脚本,它们都是composer.phar的包装器。Windows下实际总是使用composer.bat脚本(输入时可以省略后缀名)

用户环境变量path:
C:\Users\Administrator\AppData\Roaming\Composer\vendor\bin
这个路径用来寻找到全局安装的二进制命令(或者是脚本),上上级目录(Composer)也是全局全局项目的位置,所有全局的依赖都会写入到Composer/composer.json文件。

更换镜像地址:
composer config repo.packagist composer https://packagist.phpcomposer.com
以上命令把镜像更换为中国大陆的地址,会在C:\Users\Administrator\AppData\Roaming\Composer中放入auth.json、cacert.pem、config.json、keys.dev.pub、keys.tags.pub文件,这些文件都是针对全局配置而引入的,关键是config.json文件,这个文件最终会和具体项目的composer.json合并(从而实现更换镜像地址)。

另外:
Composer会缓存安装包,在Windows下,安装包缓存的位置为:C:\Users\Administrator\AppData\Local\Composer\files
Linux下,composer相关的配置缓存,都在用户目录的.config/composer目录中。
#########################################

–Windows下Composer提供了一个安装包(https://getcomposer.org/Composer-Setup.exe),这个玩意安装完成后
composer-win
玛尼,这个是啥玩意,一个卸载程序,一个安装程序而已(最新安装版本,不会再拷贝安装文件)。实际使用的东西在:
composer-setup
路径:C:\ProgramData\ComposerSetup\bin已经自动注册到了系统的PATH中,意味着可以直接运行composer.bat(composer是SHELL脚本),它实际是composer.phar的包装器,这样就可以在命令行中运行composer命令(之所以需要包装器,那是因为#!/usr/bin/env php指令在Windows下无效)。

Windows下的安装还会注册一个用户级别的PATH环境变量,指向到C:\Users\Administrator\AppData\Roaming\Composer\vendor\bin,实际上使用Composer全局安装时,文件会安装到C:\Users\Administrator\AppData\Roaming\Composer路径,其中的vendor\bin就是全局安装包提供的命令行工具。

实际上,也可以直接去下载composer.phar放入到根目录下,然后定位到这个目录,运行php composer.phar ***, 不过由于Windows下有些特殊,所以还是使用Windows下安装方法为好。
——Windows下安装——————————————–

任何一个项目下,都可以放置一个config.json的文件,它会会composer.json合并,而且对应config字段,对于全局安装可以在C:\Users\Administrator\AppData\Roaming\Composer中放入config.json(Linux: ~/.config/composer/config.json):

{
    "config": {
        "preferred-install": "dist",
        "secure-http": false
    },
    "repositories": [
        {"type": "composer", "url": "http://packagist.phpcomposer.com"},
        {"packagist": false}
    ]
}

修改为首先从本地缓存取数据,然后把仓库改为中国镜像(http://packagist.phpcomposer.com)。本地缓存在Windows下存放在C:\Users\Administrator\AppData\Local\Composer中。

以上配置是通过把仓库指定到国内镜像,否则下载可能非常缓慢。如果国内镜像不可用,或者希望直接使用原始仓库,可以设置HTTP_PROXY环境变量,让其走代理:
http_proxy
也可以命令行下运行:SET HTTP_PROXY=http://127.0.0.1:8087

composer.phar是composer的主程序,还有一个文件是用来存储依赖关系的文件,一般在项目根下建立一个叫composer.json的文件,里面说明需要使用的库:

{
    "require": {
        "monolog/monolog": "1.0.*"
    }
}

然后开始安装:

[root@vfeelit]# ./composer.phar install
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing monolog/monolog (1.0.2)
    Downloading: 100%         

Writing lock file
Generating autoload files

这样在当前目录下,就多了一个叫verdor的文件夹,依赖关系库文件也下载在其中。

composer.json文件的基本设置:
require关键字
require关键字来告诉composer哪些包是你项目所需要的

{
    "require": {
        "monolog/monolog": "1.0.*"
    }
}

如上所示,require的对象将会映射包的名称( monolog/monolog)和包的版本是1.0.*。

基本上包的命名是 主名/项目名称(monolog/monolog),主名必须唯一,但是项目也就是我们的包的名称可以有相同的(这个不需要解释)。

接下来是包的版本,比如使用monolog的版本是1.0.*,意思是只要版本是1.0分支即可,例如1.0.0,1.0.2等。

版本定义的方式:
1)标准的版本:定义保准的版本包文件,如:1.0.2
2)一定范围的版本:使用比较符号来定义有效的版本的范围,有效的符号有>, >=, <, <=, !=
3)通配符:特别的匹配符号*,例如1.0.*就相当于>=1.0,<1.1版本的即可
4)下一个重要的版本:~符号最好的解释就是,~1.2就相当于>1.2,<2.0,但~1.2.3就相当于>=1.2.3,<1.3版本。

—————————————————————
composer.lock文件
设置好composer.json后,运行composer.phar install就会下载对应的依赖包,之后会生成一个叫composer.lock的文件。

注意,在使用install命令时,它首先会判断composer.lock文件是否存在,如果存在,将会下载相对应的版本(忽略composer.json里面的配置),这意味着任何下载项目的人都将会得到一样的版本。如果不存在composer.lock,composer将会通过composer.json来读取需要的包和相对的版本,然后创建composer.lock文件。这样,如果依赖包有新的版本后,就不会被更新到新的版本,可以使用update命令来完成更新,运行了update后将获取到最新的版本同时composer.locak文件也被更新。

php composer.phar update

Packagist仓库
在运行composer.phar install时,对应的依赖包会被下载,那么从哪里下载,这些包存储在什么地方?答案是在https://packagist.org。Packagist是composer的主要仓库,composer仓库包含了包的源码,Packagist的目的建成一个任何人都可以使用的仓库。

在运行composer.phar install下载了对应的依赖包后,在vendor下会生成autoload.php文件,只要:

require 'vendor/autoload.php';

就可以使用依赖包了。

以下以如何使用ZF2类库进行稍微深入的分析:

// 文件vendor/autoload.php
<?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer' . '/autoload_real.php';

return ComposerAutoloaderInit3c4646160e3ed938b9236365912a65f4::getLoader();

这个哈希码是composer的版本号,这个vendor/composer/autoload_real.php实际就定义了ComposerAutoloaderInit3c4646160e3ed938b9236365912a65f4类:

<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInit3c4646160e3ed938b9236365912a65f4
{
    private static $loader;

    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }
	//把loadClassLoader方法注册  专门用来加载Composer\Autoload\ClassLoader类
	//注意,最后一个参数表示把这个函数放到堆栈的最前面
	//http://blog.ifeeline.com/169.html
        spl_autoload_register(array('ComposerAutoloaderInit3c4646160e3ed938b9236365912a65f4', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
	//销毁 只需要用一次  现在获得了Composer\Autoload\ClassLoader实例
        spl_autoload_unregister(array('ComposerAutoloaderInit3c4646160e3ed938b9236365912a65f4', 'loadClassLoader'));

        $vendorDir = dirname(__DIR__);	//__DIR__为vendor/composer  那么目录就是vendor
        $baseDir = dirname($vendorDir);	//就是项目根目录

	//autoload_namespaces.php定义了命名空间对应的路径,文件内容:
	//return array(
	//    'Zend\\' => array($vendorDir . '/zendframework/zendframework/library'),
	//    'ZendXml' => array($vendorDir . '/zendframework/zendxml/library'),
	//);
        $map = require __DIR__ . '/autoload_namespaces.php';
        foreach ($map as $namespace => $path) {
            $loader->set($namespace, $path);
        }

	//使用psr4方式
	//return array(
	//);
        $map = require __DIR__ . '/autoload_psr4.php';
        foreach ($map as $namespace => $path) {
            $loader->setPsr4($namespace, $path);
        }

	//classmap 映射类
        $classMap = require __DIR__ . '/autoload_classmap.php';
        if ($classMap) {
            $loader->addClassMap($classMap);
        }
	//public function register($prepend = false)
	//{
	//    spl_autoload_register(array($this, 'loadClass'), true, $prepend);
	//}
	//最终回到$loader对象的loadClass加载对象 这个逻辑不再深入
        $loader->register(true);
	//这个方法最终返回$loader
        return $loader;
    }
}

function composerRequire3c4646160e3ed938b9236365912a65f4($file)
{
    require $file;
}

从以上分析可知:

require 'vendor/autoload.php';

这个require最终返回$load对象,从分析可以知道,在加载类的时候,使用到了$loader->set($namespace, $path),而这个对应关系来自autoload_namespaces.php文件,这个文件的对应关系又是通过运行composer update时通过composer.json的配置来写入的(composer还负责下载对应的库文件)。如果仅仅希望使用Composer的类自动装载功能,可以如下使用:

//比如lib下有一个叫Vfeelit的包,如果要自动装载,只要这样干
$loader->set('Vfeelit','xxx/lib');    // Vfeelit_Test
$loader->set('Vfeelit_','xxx/lib');   // Vfeelit_Test
$loader->set('Vfeelit\\','xxx/lib');  // Vfeelit\Test

注意:一些自定义的库,可以通过放入一个配置文件中(格式参考autoload_namespaces.php),然后在代码中添加类似如下的代码:

        $map = require __DIR__ . '/autoload_namespaces.php';
        foreach ($map as $namespace => $path) {
            $loader->set($namespace, $path);
        }

$loader对象还提供了add()和addPrs4()方法(明显add()就是指prs0),它跟set()和setPrs4()是不一样的。具体来说,set是设置一个命名空间对应一个路径,对应命名空间,通过set只能对应一个目录,但是通过add就可以设置一个命名空间对应多个路径(A路径搜索不到就搜索B路径,看起来意义不大,有意义的是针对空命名空间时,空命名空间搜索多个目录就像require搜索include_path一样)。另外,set()和add()方法的第一参数是命名空间字符串,实际上它是可以为空的,就相当于空命名空间对应的搜索路径。

$loader->add('',$library);

$o = new Vfeelit\Test();
$o = new Vfeelit_TTest();

另外,为了在没有找到类时,去搜索PHP的include_path,可以设置$loader->setUseIncludePath(true),默认是不去搜索include_path的(一般不再使用这个方法,一些旧的类库大量使用了require时,比如zf1.x,就必须设置include_path)。例子:

<?php
$rootPath = dirname(__DIR__);
$library = $rootPath."/library";

///
$includePath = array(
	$library,
	get_include_path()
);
set_include_path(implode(PATH_SEPARATOR, $includePath));

///
chdir($rootPath);
if(file_exists('vendor/autoload.php')){
	$loader = include '/vendor/autoload.php';
}else{
	exit("Autoload Failed. ");
}

///
$loader->setUseIncludePath(true);

///
$loader->add("Vfeelit\\",$library);
$loader->add("Vfeelit",array($library)); // 可以指定数组,表示对应多个路径

///
$test = new \Vfeelit\Test();
$test = new Vfeelit_TTest();

这样,library下的库就会自动被装载(不需要去设置对应关系)。类的自动装载的顺序是先具体后一般,具体来说就是如果找到类的映射,就直接使用,如果找不到就寻找是否有命名空间的映射,再找不到就去搜索include_path(如果启用了)。

一般,我们使用composer主要用来管理库,准确来说是对库的版本更新进行管理,库类的自动装载是其组成部分,所以应该重复利用它对库的强大管理能力,当然,对应一些不在composer仓库中的自定义库,也是可以使用composer提供的自动装载功能的。一般,如果要更新类库(比如有了新版本),只要修改composer.json,然后运行composer.phar update即可更新。

永久链接:http://blog.ifeeline.com/1075.html
原创文章,转载务必保留出处。

速卖通API接入之授权二

之前曾写过“速卖通API接入之授权”,这次是写点有关授权的官方文档中没有的内容。

一般,如果你自己想开发一个程序,这个程序需要用到速卖通的API,那么你首先需要开一个速卖通账户,用这个账户去申请接入的相关信息。

如果你不想开发,那么你可以用第三方的程序,你不需要自己去申请API接入的相关信息,但是你要授权这个第三方程序它通过API来抓取你的店铺的数据。

很简单,一个是要你自己去申请接入的Key和秘钥,然后调用速卖通的API抓取数据;一个是不需要自己去搞这些,委托第三方来通过API来抓取数据。实际上,这里描述的这两种方式,对应于“App授权协议”的自用 和 商用,简单来说就是,如果是自用的,那么你开发的程序就仅仅用来抓取你的店铺的数据,如果是商用的,你的程序可以被其它账户委托,用来抓取别人店铺的数据。

那么你申请了一个自用账户,开发了一个程序,完了之后去接受别人的委托,然后去抓取别人的信息,是否可以呢? 至少从理论上来说,应该是可以的。关键就在于速卖通到底有没有严格执行自用 与 商用的逻辑。比如,如果你是自用的,就限制不可以接受委托,怎么进行呢?

我们知道,为了获取授权,通常有如下:
速卖通授权
在验证用户和密码的同时验证过来的Appkey是否跟这个用户一致,就可以限制自用。

不过,到目前为止,这个逻辑并没有实施。那么也就是说,自用的程序,搞完之后,也可以接受别人的委托抓别人的数据。这样的好处在于,如果你有多个店铺,那么只要拿一个去申请自用的Appkey和秘钥,其它的店铺就不需要了,因为它们可以委托这个已经存在的App去抓取数据。还有一个原因是,如果一个新店铺,如果还没有交易,那么是无法申请自用App的(也就是无法使用API),但是通过委托的方式就可以,所以对于新店铺,就可以在没有交易的情况下,通过自己的程序去铺产品。

速卖通目前只能是自用就自用,说起来是让有资质的第三方公司来做这种专门的开发,实际是卡主这里,和第三方应用进行应用的利润分成。

永久链接:http://blog.ifeeline.com/1071.html
原创文章,装载务必保留出处。