存档在 ‘web前端’ 分类

正确的 Composer 扩展包安装方法

2017年5月22日

问题说明

我们经常要往现有的项目中添加扩展包,有时候因为文档的错误引导,如下图来自 这个文档 的:

file

composer update 这个命令在我们现在的逻辑中,可能会对项目造成巨大伤害。

因为 composer update 的逻辑是按照 composer.json 指定的扩展包版本规则,把所有扩展包更新到最新版本,注意,是 所有扩展包,举个例子,你在项目一开始的时候使用了 monolog,当时的配置信息是

"monolog/monolog": "1.*"

安装的是 monolog 1.1 版本,而一个多月以后的现在,monolog 已经是 1.2 了,运行命令后直接更新到 1.2,这时项目并没有针对 1.2 进行过测试,项目一下子变得很不稳定,情况有时候会比这个更糟糕,尤其是在一个庞大的项目中,你没有对项目写完整覆盖测试的情况,什么东西坏掉了你都不知道。

那应该使用哪个命令呢?install, update 还是 require ?

接下来我们一一解释。

简单解释

composer install - 如有 composer.lock 文件,直接安装,否则从 composer.json 安装最新扩展包和依赖;
composer update - 从 composer.json 安装最新扩展包和依赖;
composer update vendor/package - 从 composer.json 或者对应包的配置,并更新到最新;
composer require new/package - 添加安装 new/package, 可以指定版本,如: composer require new/package ~2.5.

流程

下来介绍几个日常生产的流程,来方便加深大家的理解。

流程一:新项目流程
创建 composer.json,并添加依赖到的扩展包;
运行 composer install,安装扩展包并生成 composer.lock;
提交 composer.lock 到代码版本控制器中,如:Git;

流程二:项目协作者安装现有项目
克隆项目后,根目录下直接运行 composer install 从 composer.lock 中安装 指定版本 的扩展包以及其依赖;

此流程适用于生产环境代码的部署。

流程三:为项目添加新扩展包

使用 composer require vendor/package 添加扩展包;
提交更新后的 composer.json 和 composer.lock 到代码版本控制器中,如:git;

关于 composer.lock 文件

composer.lock 文件里保存着对每一个代码依赖的版本记录(见下图),提交到版本控制器中,并配合composer install 使用,保证了团队所有协作者开发环境、线上生产环境中运行的代码版本的一致性。

这里写图片描述

关于扩展包的安装方法

那么,准备添加一个扩展包,install, update, require 三个命令都可以用来安装扩展包,选择哪一个才是正确的呢?

答案是:使用 composer require 命令

另外,在手动修改 composer.json 添加扩展包后,composer update new/package 进行指定扩展包更新的方式,也可以正确的安装,不过不建议使用这种方法,因为,一旦你忘记敲定后面的扩展包名,就会进入万劫不复的状态,别给自己留坑呀。

上面的概念不论对新手或者老手来说,都比较混淆,主要记住这个概念:

原有项目新添加扩展的,都使用 composer require new/package 这种方式来安装。

需要加版本的话

composer require "foo/bar:1.0.0"

更新指定扩展到指定版本

有时候你之前使用过的扩展包,加入了新功能,你想更新单独这个扩展包到指定版本,也可以使用 require 来操作。

如下面例子,需要更新 “sami/sami”: “3.0.” 到 “sami/sami”: “3.2.”
这里写图片描述

命令行运行:
这里写图片描述

一个更容易检测设备的JavaScript库——device.js

2017年4月28日

decice.js的github地址(https://github.com/matthewhudson/device.js/)

device.js是一个可以用来检测设备的平台、操作系统和方向的JavaScript库。device.js 通过操作系统(比如 iOS,安卓,黑莓,Windows,Firefox OX),方向(横屏或者竖屏),类型(平板或者移动设备),来为设备添加 CSS Class,并且它还提供了一些Javascript 函数用来判断设备。
比如在PC端打开引入了device.js的 html 页面时会在标签里添加”desktop landscape”的class。
device.js的使用
直接在html页面的头部引入即可使用:

<script type="text/javascript" src="device.js"></script>

根据设备的不同生成的CSS
**Device** **CSS Classes**

iPhone ios iphone mobile

iPod ios ipod mobile

Android Phone android mobile

Android Tablet android tablet

BlackBerry Phone blackberry mobile

BlackBerry Tablet blackberry tablet

Windows Phone windows mobile

Windows Tablet windows tablet

Firefox OS Phone fxos mobile

Firefox OS Tablet fxos tablet

MeeGo meego

Desktop desktop

Television television

根据方向的不同生成的CSS
**Orientation** **CSS Classes**

Landscape landscape

Portrait portrait

相关的JavaScript方法
**Device** **JavaScript Method**

Mobile device.mobile()

Tablet device.tablet()

Desktop device.desktop()

iOS device.ios()

iPad device.ipad()

iPhone device.iphone()

iPod device.ipod()

Android device.android()

Android Phone device.androidPhone()

Android Tablet device.androidTablet()

BlackBerry device.blackberry()

BlackBerry Phone device.blackberryPhone()

BlackBerry Tablet device.blackberryTablet()

Windows device.windows()

Windows Phone device.windowsPhone()

Windows Tablet device.windowsTablet()

Firefox OS device.fxos()

Firefox OS Phone device.fxosPhone()

Firefox OS Tablet device.fxosTablet()

MeeGo device.meego()

Television device.television()

**Orientation** **JavaScript Method**
Landscape device.landscape()
Portrait device.portrait()
通常情况下,我们为了使页面在不同分辨率的设备上展示出不同的效果,会使用CSS3的 @media属性来实现,但如果我们想在 PC端和 mobile端展示两个不同的页面,使用device.js 就会方便很多,首先用它来检测设备,然后再在不同的设备上打开不同的页面。
假设有个项目,我们想让它在手机上打开的页面为 m.html,在电脑上打开的页面为 desk.html,这个时候我们就可以用device.js来实现,代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <title>device.js的使用</title>
    <script type="text/javascript" src="device.js"></script>
</head>
<body>
    <script type="text/javascript">
        var isMobile = device.mobile(),
             isTable = device.tablet();

        if(isMobile || isTable){
            window.open("m.html","_self"); //如果终端是手机或者平板,就打开m.html
        }
        else{
            window.open("desk.html","_self"); //否则打开desk.html
        }
    </script>
</body>
</html>

MAC下node.js安装路径

2017年2月23日

install : node-v7.6.0
mkdir : /usr/local/n/versions/node/7.6.0
fetch : https://nodejs.org/dist/v7.6.0/node-v7.6.0-darwin-x64.tar.gz

Node.js was installed at
/usr/local/bin/node
npm was installed at
/usr/local/bin/npm
Make sure that /usr/local/bin is in your $PATH.

引入百度地图js时产生的问题

2017年2月15日

页面引入百度地图API:

<script type="text/javascript" src="http://api.map.baidu.com/api?v=1.3&ak=XhxWo4lQh2KfH3phyElA17ljQC1wwqjT"></script>

产生如下问题:

api:1 A Parser-blocking, cross-origin script, http://api.map.baidu.com/getscript?v=1.3&ak=XhxWo4lQh2KfH3phyElA17ljQC1wwqjT&services=&t=20150527115231, is invoked via document.write. This may be blocked by the browser if the device has poor network connectivity.
即为报错:A Parser-blocking, cross-origin script, is invoked via document.write.

在页面渲染完成后就不能使用 document.write 方法。
根据博客的说明尝试如下方法:

<script>
            //异步加载百度地图,解决谷歌等浏览器在页面渲染完成后就不能使用 document.write 方法的问题:
            var cnzz_s_tag = document.createElement('script');
            cnzz_s_tag.type = 'text/javascript';
            cnzz_s_tag.async = true;
            cnzz_s_tag.charset = 'utf-8';
            cnzz_s_tag.src = 'http://api.map.baidu.com/api?v=1.3&ak=XhxWo4lQh2KfH3phyElA17ljQC1wwqjT&async=1';
            var root_s = document.getElementsByTagName('script')[0];
            root_s.parentNode.insertBefore(cnzz_s_tag, root_s);
        </script>

出现问题:

api:1 Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.

后来发现页面引入的百度的js内容为:

(function(){ 
        window.BMap_loadScriptTime = (new Date).getTime(); 
        document.write('<script type="text/javascript" src="http://api.map.baidu.com/getscript?v=1.3&ak=XhxWo4lQh2KfH3phyElA17ljQC1wwqjT&services=&t=20150527115231"></script>');
        document.write('<link rel="stylesheet" type="text/css" href="http://api.map.baidu.com/res/13/bmap.css" />');
})();

里面包含了 document.write方法,异步加载的js是不允许使用document.write方法的

直接引用这两个地址的js

http://api.map.baidu.com/getscript?type=quick&file=api&ak=XXXX&t=20140109092002和 http://api.map.baidu.com/getscript?type=quick&file=feature&ak=XXXX&t=20140109092002

就可以了

thinkPHP绑定入口文件之后 配置的子域名不生效 解决办法

2017年2月15日

摘要: thinkPHP在入口文件绑定模块后 //入口文件绑定模块 define(‘BIND_MODULE’,’Home’); 在App/Common/Conf/config.php ‘APP_SUB_DOMAIN_DEPLOY’ => 1, // 开启子域名配置 ‘APP_SUB_DOMAIN_RULES’ => array( ‘wap.xxxx.com’ => ‘Wap’, ), 不生效 解决办法/blockquote>

thinkPHP在入口文件绑定模块后

//入口文件绑定模块

define('BIND_MODULE','Home');
在App/Common/Conf/config.php 

    'APP_SUB_DOMAIN_DEPLOY'   =>    1, // 开启子域名配置
	'APP_SUB_DOMAIN_RULES'    =>    array(   
	    'wap.xxxx.com'  => 'Wap',  
	),

不生效 解决办法

解决办法就是:

不用在入口文件绑定模块

这样

直接在config中这样写子域名

    'APP_SUB_DOMAIN_DEPLOY'   =>    1, // 开启子域名配置
	'APP_SUB_DOMAIN_RULES'    =>    array(   
	    'wap.xxxx.com'  => 'Wap',  
	    'www.xxxx.com'  => 'Home',
	),

问题解决

thinkphp下配置和使用阿里云redis

2017年2月15日

1.redis简介
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库。(未完待续…)
2.thinkphp端配置
config.php里的相关配置:

// 阿里云redis
    'DATA_CACHE_TYPE'                   => 'Redis',
    'REDIS_HOST'                        => '123456789',
    'REDIS_PORT'                        => 6379,
    'DATA_CACHE_TIME'                   => 3600,
    'REDIS_AUTH'                        => '123456789',

REDIS_HOST和REDIS_AUTH 在购买阿里云redis时会得到,把相应的值配置到config.php就行。
3.php端redis的使用
我现在的项目中只用到了redis的两个基本操作:set和get方法,redis的用法比较简单,当然还有其它高级的用法。
set方法:

    public static function setage($name,$age)
    {    
        $redis = new \Redis();
        $redis->connect(C("REDIS_HOST"), C("REDIS_PORT"));
        $redis->auth(C("REDIS_AUTH"));    
        $redis->set("age:".$name, $age);
    }

redis是k—v型数据库, $redis->set(“age:”.$username, $age); 这样存的目的是,更加形象一点,存下来的数据是这样的,age:小明 -> 16
get方法:

    public static function getage($name)
    {    
        $redis = new \Redis();
        $redis->connect(C("REDIS_HOST"), C("REDIS_PORT"));
        $redis->auth(C("REDIS_AUTH"));
        $age=$redis->get("age:".$name);
        if (!$age) {
            return false;
        }
        return $age;
    }

get方法,这时如果$name值传过来的是 小明 就会查到 age:小明 -> 16 ,返回16。

基于ThinkPHP3.2.3下载文件(几百M以上的)

2017年2月11日

控制器部分

   function downfile($fileurl='http://www.fuhuaqi.com/Upload/activity/mongo.rar')
    {
//        下载本地方式一:会受到文件大小限制
//        ob_start();
//        $filename=$fileurl;
//        $date=date("Ymd-H:i:m");
//        header( "Content-type:  application/octet-stream ");
//        header( "Accept-Ranges:  bytes ");
//        header( "Content-Disposition:  attachment;  filename= {$date}.zip");
//        $size=@readfile($filename);
//        header( "Accept-Length: " .$size);

//        下载本地方式二:目前下载500M左右的都没问题,就是时间太慢
        set_time_limit(0);
        ini_set('memory_limit', '512M');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename='.basename($fileurl));
        header('Content-Transfer-Encoding: binary');
        ob_end_clean();
        $size = readfile($fileurl);
        header( "Accept-Length: " .$size);
    }

方法三:下载之后在指定的地方,但是没有返回,成功之后只是一个空页面,使用TP的HTTP类

//下载之后在指定的地方,但是没有返回,只是一个空页面

 public function downmy()
    {

        $tu = time() . 'mygoods' . ".zip";
        $Http = new Http();
        $Http->curlDownload("http://www.fuhuaqi.com/Upload/activity/111111.zip", "./Upload/activity/".$tu);
        $url = "http://www.fuhuaqi.com/Upload/activity/".$tu;
        return $url;
    }

视图部分

<td><a class="fa fa-chevron-circle-down" href="__ROOT__/Admin/Bpapply/downfile">下载</a></td>

修改1.0.1

function downfile()
{
    ob_start();
    $id = I('get.id');
    $bpapplys = D('Bpapply')->selectOne($id);
    //上线后需要修改路径为绝对路径,否则下载不全
    $fileurl="http://www.fuhuaqi.com/Upload/bpapply/".$bpapplys['bp_bpimg'];
    set_time_limit(0);
    ini_set('memory_limit', '512M');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($fileurl));
    header('Content-Transfer-Encoding: binary');
    ob_end_clean();
    $size = readfile($fileurl);
    header( "Accept-Length: " .$size);
}

ThinkPHP3.2.3新特性之:数据库设置

2016年12月15日

ThinkPHP3.2.3版本数据库驱动采用PDO完全重写,配置和使用上面也比之前版本更加灵活和强大,我们来了解下如何使用。

首先,3.2.3的数据库配置信息有所调整,完整的数据库设置包括:

/* 数据库设置 */
 'DB_TYPE'               =>  '',     // 数据库类型
 'DB_HOST'               =>  '', // 服务器地址
 'DB_NAME'               =>  '',          // 数据库名
 'DB_USER'               =>  '',      // 用户名
 'DB_PWD'                =>  '',          // 密码
 'DB_PORT'               =>  '',        // 端口
 'DB_PREFIX'             =>  '',    // 数据库表前缀
 'DB_PARAMS'          =>  array(), // 数据库连接参数
 'DB_DEBUG'  =>  TRUE, // 数据库调试模式 开启后可以记录SQL日志
 'DB_LITE'             =>  false,    // 使用数据库Lite模式
 'DB_FIELDS_CACHE'       =>  true,        // 启用字段缓存
 'DB_CHARSET'            =>  'utf8',      // 数据库编码默认采用utf8
 'DB_DEPLOY_TYPE'        =>  0, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
 'DB_RW_SEPARATE'        =>  false,       // 数据库读写是否分离 主从式有效
 'DB_MASTER_NUM'         =>  1, // 读写分离后 主服务器数量
 'DB_SLAVE_NO'           =>  '', // 指定从服务器序号

相对3.2.2版本来说,取消了如下设置参数:

'DB_FIELDTYPE_CHECK' // 3.2.3强制进行字段类型检测了
 'DB_SQL_BUILD_CACHE' // 3.2.3取消了SQL创建缓存
 'DB_SQL_BUILD_QUEUE' // 3.2.3取消了SQL创建缓存
 'DB_SQL_BUILD_LENGTH' // 3.2.3取消了SQL创建缓存
 'DB_SQL_LOG' // 由新增的DB_DEBUG参数取代
 'DB_BIND_PARAM' // 新版采用PDO 自动参数绑定 无需设置

新增的数据库设置参数包括:

‘DB_DEBUG’ //用于开启数据库调试模式,开启后即可记录SQL日志
‘DB_LITE’ // 是否采用数据库Lite模式连接 开启后只能使用原生SQL查询
3.2.2版本数据库的调试模式和项目的调试模式(由APP_DEBUG常量定义)是绑定的 ,3.2.3版本开始数据库的调试模式是独立设置(由DB_DEBUG参数设置)的。

DB_TYPE参数为数据库类型设置,目前支持的驱动包括mysql/sqlite/oracle/pgsql/sqlsrv/firebird(其他的数据库类型需要增加驱动),设置如下:

‘DB_TYPE’=>’mysql’, // 不再支持设置为PDO 也不再区分mysql和mysqli
数据库的连接信息,主要包括下面参数:

'DB_HOST'               =>  '', // 服务器地址 采用IP地址
 'DB_NAME'               =>  '',          // 数据库名
 'DB_USER'               =>  '',      // 用户名
 'DB_PWD'                =>  '',          // 密码
 'DB_PORT'               =>  '',        // 端口 留空则取默认端口
 'DB_CHARSET'            =>  '',      // 数据库编码

以上设置参数会在实例化PDO的时候自动转换为PDO的连接参数传入。

DB_DSN参数一般无需设置,系统的数据库驱动会进行默认设置,如果需要调整,请遵循PDO的相关数据库连接的DSN设置进行设置。

DB_PARAMS用于设置数据库的连接参数,会传入PDO实例化的第四个参数。

下面是一个典型的数据库全局设置:

'DB_TYPE'               =>  'mysql',     // 数据库类型
 'DB_HOST'               =>  '192.168.1.10', // 服务器地址
 'DB_NAME'               =>  'thinkphp',          // 数据库名
 'DB_USER'               =>  'root',      // 用户名
 'DB_PWD'                =>  '1234',          // 密码
 'DB_PORT'               =>  '3306',        // 端口
 'DB_PREFIX'             =>  'think_',    // 数据库表前缀
 'DB_CHARSET'            =>  'utf8',      // 数据库编码
 'DB_DEBUG'  =>  TRUE, // 数据库调试模式 开启后可以记录SQL日志

如果在模型类中设置单独的数据库连接信息connection属性,可以使用下面的数组或者字符串方式:

//在模型里单独设置数据库连接信息

namespace Home\Model;
 use Think\Model;
 class UserModel extends Model{
 // 采用数组方式定义
    protected $connection = array(
        'db_type'  => 'mysql',
        'db_user'  => 'root',
        'db_pwd'   => '1234',
        'db_host'  => '192.168.1.10',
        'db_port'  => '3306',
        'db_name'  => 'thinkphp',
        'db_charset' =>    'utf8',
    );
 }

注意:在模型中设置的数据库连接设置参数采用全局配置的小写名。

或者采用字符串方式定义,格式为:
数据库类型://用户名:密码@数据库地址:数据库端口/数据库名#字符集
例如:

//在模型里单独设置数据库连接信息

namespace Home\Model;
 use Think\Model;
 class UserModel extends Model{
    // 使用字符串方式定义
    protected $connection = 'mysql://root:1234@192.168.1.10:3306/thinkphp#utf8';
 }

也可以通过配置文件设置,例如:

//数据库配置1

'DB_CONFIG1' => array(
    'db_type'  => 'mysql',
    'db_user'  => 'root',
    'db_pwd'   => '1234',
    'db_host'  => '192.168.1.10',
    'db_port'  => '3306',
    'db_name'  => 'thinkphp',
    'db_charset'=>    'utf8',
 ),

//数据库配置2

'DB_CONFIG2' => 'mysql://root:1234@192.168.1.10:3306/thinkphp#utf8';

然后在模型里面定义:

//在模型里单独设置数据库连接信息

namespace Home\Model;
 use Think\Model;
 class UserModel extends Model{
    //调用配置文件中的数据库配置1
    protected $connection = 'DB_CONFIG1';
    // 或者
    protected $connection = 'DB_CONFIG2';
 }

除了在模型定义的时候指定数据库连接信息外,我们还可以在实例化的时候指定数据库连接信息,如果采用的是M方法实例化模型的话,也可以支持传入不同的数据库连接信息,例如:

$User = M('User','other_','mysql://root:1234@192.168.1.10/demo#utf8'); 

表示实例化User模型,连接的是demo数据库的other_user表,采用的连接信息是第三个参数配置的。
如果我们在项目配置文件中已经配置了DB_CONFIG2的话,也可以采用:

$User = M('User','other_','DB_CONFIG2');