标签归档:PHP

解决 protobuf 3 PHP 中枚举值报错问题

前两天在项目中突然遇到一个protobuf php库报错,报错为:

"Undefined offset: 8","context"

这个一看就是直接是索引下标,找不到值就超出报错了,再往前回溯一下报错,发现这个错误是

Google\\Protobuf\\Internal\\Message

报出的。后来看了项目的.proto文件,发现是其他项目的同事更新了这个proto文件,把一个结构体的枚举值添加了一个,导致我们这边项目在收到这个结构体的时候无法识别。此时其实只要更新proto文件就可以解决问题,但是这样并不能一劳永逸,下一次遇到了枚举值增加的时候依旧会出现。

不过按说谷歌的protobuf php库用了这么多年应该没什么大问题,回溯一下代码发现在

https://github.com/protocolbuffers/protobuf/blob/master/php/src/Google/Protobuf/Internal/EnumDescriptor.php

这行代码的问题,这里直接使用的是,没有做异常处理。

  return $this->value[$number];

再接着经过同事的发现,这个问题已经解决了,也已经有人提了pr

https://github.com/protocolbuffers/protobuf/pull/6155#issuecomment-493795218

只需要把依赖升级一下即可,之前使用的是3.6.*,直接升级到3.10.*就可以解决问题。

如何高效的获取远程图片的长宽、格式、文件大小

最近在做一个图片抓取系统,其中有一个地方是需要用到获取远程图片的长宽,大小,和格式信息;不过一提到获取这样的信息,那么脑海里马上就会浮现 getimagesize() 这个函数,但是,在真正用到的时候才发现,这个函数很笨,效率很低,一方面是它会把这个图片直接下载下来,然后再获取其中的信息,而我们又知道,下载东西,CURL肯定是迅猛无比的;那么能不能把这两者结合起来呢~

但是问题又出现了,如果我用CURL的方式,就不太容易获取图片长宽信息了,不够方便,于是乎我就翻阅了文档中和图片有关的函数,就找到了这么一个getimagesizefromstring(),刚好符合我的要求!于是乎你只需要下面这个函数就可以比较高效的获取啦!

public function get_remote_filesize($url){
       $ch = curl_init ();
       curl_setopt ( $ch, CURLOPT_CUSTOMREQUEST, 'GET' );
       curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, false );
       curl_setopt ( $ch, CURLOPT_URL, $url );
       ob_start ();
       curl_exec ( $ch );
       $return_content = ob_get_contents ();
       ob_end_clean ();
       $return_code = curl_getinfo ( $ch, CURLINFO_HTTP_CODE );
       $len = floatval( round(strlen($return_content)/1024/1024,2) );
       echo $len;
       var_dump(getimagesizefromstring($return_content));
}

说点别的,其实获取远程图片信息有很多种方法,比如file_get_content(),比如使用get_header(),获取页面头中的信息来判定图片,但如果对方服务器不允许获取header信息,那就不行了。所以相比而下依旧是CURL效率最快,而且CURL可以模拟各种请求,高效而强大,在批量下载文档,或者图片之类的,CURL方法仍是首选。

Laravel 添加 Oracle 数据库常驻连接( DRCP )

由于以前一直接触的是互联网业务,所以使用 Mysql 数据库的比较多,也比较熟悉。但是现有的金融公司一般使用的都是比较稳定的 Oracle 解决方案,所以在用户量较大的情况下会遇到一个比较麻烦的问题:Oracle 下的高并发。

问题描述:由于同时访问某个应用的用户量非常大;导致后端不断查询数据库,虽然每个 sql 语句的查询时间很短,但是由于要不断的连接->查询->断开,所以Oracle服务器会发现很多的查询进程,一个接一个,由于每个sql执行的时候,服务器会分配一些资源和内存给这个查询,所以在并发非常大的时候,悲剧就产生了,CPU和内存的使用率马上被推上峰值,负载马上压到最大,然后所有的 sql 查询全部变慢,变慢导致的后果是,应用服务器下的 Apache 进程时间越来越长,由于用户还在不断的查询,所以后面进入的apache进程会一直处于等待状态,而正在执行的进程缺越来越慢,果不其然的是很快,应用服务器马上也被拖垮。

解决方案:实际上这样的情况有很多方法优化,此处就着重描述一下在有限的服务器资源的状况下引入的一个数据库驻留连接池的特性,当然,这个特性 JAVA 已经自带了,而 PHP 需要修改一些配置,也可以使用。

数据库驻留连接池是 Oracle Database 11g 的一个新特性。对 PHP,它允许 Web 应用程序随着站点吞吐量的增长对连接数进行扩充。它还支持多台计算机上的多个 Apache 进程共享一个小规模的数据库服务器进程池。没有 DRCP,标准 PHP 连接必须启动和终止一个服务器进程。一个非 DRCP 持久性连接即使空闲时也将保留数据库服务器资源。

那么如何如何在不编写和更改任何应用程序逻辑的情况下使用这个DRCP呢?

一定要做的步骤:
要在 Oracle 中启用 DRCP,登录到数据库服务器并启动连接池:

SQL> execute dbms_connection_pool.start_pool();
PL/SQL procedure successfully completed.

通过查询特定的 DBA_CPOOL_INFO 视图确认该池已启动:

QL> SELECT CONNECTION_POOL, STATUS, MAXSIZE
  2  FROM DBA_CPOOL_INFO;

CONNECTION_POOL             STATUS		    MAXSIZE
----------------------------------------------------------
SYS_DEFAULT_CONNECTION_POOL ACTIVE		    40

OK,启动成功!接下来就是PHP的设置:
首先,打开你的php.ini文件找到

oci8.connection_class = MYPHPAPP

取消注释,起个名字;
由于这次使用的是laravel框架,所以接下来就不详细描述原生 PHP 的使用方法了(基本使用方法请猛戳这篇文章),直接进入框架使用。想要连接 Oracle 数据库,你需要安装 jfelder/laravel-oracledb 插件,代码和安装方法请猛戳这里 。安装完成了之后,你只需要找到下面这个文件

D:\work\www\baiqian_fqg\baiqian_bqfqg_wx\vendor\jfelder\oracledb\src\Jfelder\OracleDB\Connectors\OracleConnector.php

然后在这个文件中的这段代码:

(CONNECT_DATA =  (SID = {$config['database']})

中加上 (SERVER = POOLED) ,变成如下代码:

(CONNECT_DATA = (SERVER = POOLED) (SID = {$config['database']})

此举是为了声明在连接到数据库服务器的时候使用常驻池连接
接着找到这个文件

D:\work\www\baiqian_fqg\baiqian_bqfqg_wx\vendor\jfelder\oracledb\src\Jfelder\OracleDB\OCI_PDO\OCI.php

在文件中找到这段代码

    protected $attributes = array(\PDO::ATTR_AUTOCOMMIT => 1,
        \PDO::ATTR_ERRMODE => 0,
        \PDO::ATTR_CASE => 0,
        \PDO::ATTR_ORACLE_NULLS => 0
    );

此代码中加入 \PDO::ATTR_PERSISTENT => 1, 变成如下代码

    protected $attributes = array(\PDO::ATTR_AUTOCOMMIT => 1,
        \PDO::ATTR_ERRMODE => 0,
        \PDO::ATTR_CASE => 0,
        \PDO::ATTR_ORACLE_NULLS => 0,
        \PDO::ATTR_PERSISTENT => 1,
    );

此举是为了开启长连接,也可以让它默认使用oci_pconnect()函数来做为基本连接函数。
到此为止,全部更改和设置均已完成,接下来我们可以看看效果:
使用Apache自带的Apache Benchmark小工具来做个压力测试,命令如下:

ab -c 150 -t 30 http://test.php   

Apache Benchmark的介绍和用法以及参数请猛戳这里,然后使用如下语句来查询Oracle的进程数,会发现,使用常驻连接池之后比使用之前少了很多。

select username, program from v$session where username = 'PHPHOL';

(本文描述较为粗略,有一些步骤省略掉了,不过可以借此机会来涨一涨姿势,我就不啰嗦了。)

参考文档和连接:
Oracle官方文档:
http://www.oracle.com/webfolder/technetwork/tutorials/obe/db/oow10/php_db/php_db.htm#s1
Oracle官方文档(中文版)http://www.oracle.com/technetwork/cn/tutorials/229068-zhs.htm
基础使用:http://mo2g.com/view/38/
Apache Benchmark:http://blog.fabrichina.net/archives/250
laravel-oracledb:https://github.com/jfelder/laravel-oracledb

 

记一次小故障

网站后台有一个上传文件的程序,今天产品突然找到我说是不能使用了。我试了试,发现点击完上传按钮之后,老是跳转到链接被重置的页面,如下:

立马在本地查看了一下源代码,最开始的反应是文件upload_max_filesize小了或者是post_max_size的默认设置小了,查看了配置文件之后发现没什么问题,最大子好吃80M,而这个文件才23M,问题到底出在哪呢?然后前端分析了一下,发现这个提醒:

结果发现没什么用处。就再此时我突然想了一下有没有可能是后端的错误,比如是不是网络问题,服务器的空间大小问题等等,就在此时,我换了个浏览器重现了一遍错误,发现如下提醒:

413 Request Entity Too Large
The requested resource does not allow request data with the requested method or th
e amount of data provided in the request exceeds the capacity limit.

猛然想起,除了程序限制,还有NGINX限制,于是找运维,果然,限制最大为20M,改为50M,问题解决。

简单的PHP导入csv文件并解决中文为空 / 中文乱码问题

工作中有时候会遇到会把一些excel表格插入到数据库(这里是mysql)中的场景,这里有比较简单的解决方案,就是先把这个表格另存为csv格式,然后用PHP程序中的fgetcsv函数来读取。

代码如下:

function getcsv(){
    $file = fopen("1.csv","r");//文件位置
    while(! feof($file)){
        $row = _fgetcsv($file);
        echo $row['0']." | ".$row['1']." | ".$row['2'] ." | ".$row['3'] ."\r\n";//每个下标对应一列
    }
    fclose($file);
}

然后呢,就会发生一个奇怪的问题:文件中的中文要么变成了乱码,要么压根就读不出来,经过查阅,这里算是这个函数的一个小小的bug,那么,此时我们只需要重写一下这个函数就好了,如下:

 function _fgetcsv(&$handle, $length = null, $d = ',', $e = '"') {
         $d = preg_quote($d);
         $e = preg_quote($e);
         $_line = "";
         $eof=false;
         while ($eof != true) {
             $_line .= (empty ($length) ? fgets($handle) : fgets($handle, $length));
             $itemcnt = preg_match_all('/' . $e . '/', $_line, $dummy);
             if ($itemcnt % 2 == 0)
                 $eof = true;
         }
         $_csv_line = preg_replace('/(?: |[ ])?$/', $d, trim($_line));
         $_csv_pattern = '/(' . $e . '[^' . $e . ']*(?:' . $e . $e . '[^' . $e . ']*)*' . $e . '|[^' . $d . ']*)' . $d . '/';
         preg_match_all($_csv_pattern, $_csv_line, $_csv_matches);
         $_csv_data = $_csv_matches[1];
         for ($_csv_i = 0; $_csv_i < count($_csv_data); $_csv_i++) {
             $_csv_data[$_csv_i] = preg_replace('/^' . $e . '(.*)' . $e . '$/s', '$1' , $_csv_data[$_csv_i]);
             $_csv_data[$_csv_i] = str_replace($e . $e, $e, $_csv_data[$_csv_i]);
         }
         return empty ($_line) ? false : $_csv_data;
    }

然后把getcsv这个函数中的fgetcsv替换为自定义的_fgetcsv就可以了。

参考内容:http://php.net/manual/zh/function.fgetcsv.php

这些日子遇到的令人难过的BUG

最近一直都是在做还算精细的字符串处理,但是有遇到一些当时花费了好久才都没解决但是最后发现是一个php的bug的事情~

1、strtotime的BUG

在用strtotime(‘-x month’),函数往前推月份的时候,有时候会出现月份不准的情况;这是PHP的一个官方BUG,详情见此:https://bugs.php.net/bug.php?id=27793

尤其是如果涉及到的月份是31天或者是28,29天就很容易跑偏,比如:

<?php
date_default_timezone_set('Asia/Shanghai');
$t = time();
print_r(array(
            date('Y年m月',$t),
            date('Y年m月',strtotime('- 1 month',$t)),
            date('Y年m月',strtotime('- 2 month',$t)),
));

你想要的结果是这样子:

Array
(
    [0] => 2011年08月
    [1] => 2011年07月
    [2] => 2011年06月
)

但是结果却是这样子:

Array
(
    [0] => 2011年08月
    [1] => 2011年07月
    [2] => 2011年07月
)

是不是很恼火(当时就为这个推算花了一下午折腾,哭),那么解决方法呢?获取需要知道月份的第一天的时间就可以了;

代码如下:

<?php
date_default_timezone_set('Asia/Shanghai');
$first_day_of_month = date('Y-m',time()) . '-01 00:00:01';
$t = strtotime($first_day_of_month);
print_r(array(
            date('Y年m月',$t),
            date('Y年m月',strtotime('- 1 month',$t)),
            date('Y年m月',strtotime('- 2 month',$t)),
));

输出

Array
(
    [0] => 2011年08月
    [1] => 2011年07月
    [2] => 2011年06月
)

2、大小写转换函数strtolower();strtoupper()

这个是在处理验证码的时候发现的;其实这个不算是一个BUG,是一个字符编码的问题,通常验证码输入是不分大小写的,所以需要函数转换一下即可,但是不知道为什么在我本地环境是没什么问题的,上传到服务器就会发现和session里面的验证码对比不准确的奇怪事情,有时候相等有时候不相等,思来想去本地和服务器唯一不同的就是环境了,服务器是linux的,于是我当下就查阅了一下,发现一个官方的BUG:https://bugs.php.net/bug.php?id=19257 但是这个貌似不是我遇到的问题;那还有不一样的是PHP的版本,于是有查阅了,还有个版本不一样和字符集不一样的问题:

在 PHP Version 4.3.2 中,`测试`==strtolower(‘测试’)
在 PHP Version 4.3.4 中,`测试`!=strtolower(‘测试’)

然后经过查阅问题的原因是本地字符集不是中文而导致的。如果是linux系统,你只要:

export LANG=zh_CN.GB18030

就可以了。

好吧,我不管这些了;直接说最后的解决方案吧:好的就是 strncasecmp() 你没跑了,二进制安全比较字符串开头的若干个字符,自定义长度就好了;验证码就指着这个活了。

3、字符串截取函数:substr()

可能有人问了,这么基础的函数也尼玛有BUG ?

擦,真有,昨天在处理字符串的时候,需要取出一个二进制数字从右往左数的第十六位,看看是否为“1”,问题来了:因为二进制是十进制转换来的,有一些数字专函过来之后是没有十六位的,那么在window下默认就是为“0”了,但是放到linux上面就不是了,放到linux下的话,如果没有到第十六位的话它获取的字符就是最左边的那个数字,比如这样:

$action = '1000000000';//(转换为二进制之后的)
var_dump (substr($action, -16, 1) );

在本地window输出为”bool(false)”,但是放到linux下就输出为string(1) “1″ ,快哭了好么,就这个也折腾了不少时间;

但是字符还是要取出来啊,怎么办,写一个函数解决吧,翻转一下字符串正着数总好了吧:

function getbit($num, $bit) {
    $num = decbin ( $num );
     $num = strrev ( $num );
     if (substr ( $num, ($bit - 1), 1 )) {
         return 1;
     } else {
         return 0;
     }
}

参考资料:

  1. http://spamer.iteye.com/blog/1162625
  2. http://bbs.csdn.net/topics/60020464
  3. https://bugs.php.net/bug.php?id=27793
  4. https://bugs.php.net/bug.php?id=19257

PHP传值与传引用的区别

其实关于传值还是传引用这个东西,可以把它当做深入的PHP知识来看待,可以当做初学必知来看待,尤其是初学者在PHP面试当中一定会遇到的面试题之一;其实一开始我也是不明白的,直到我在搜索的过程中发现了一个绝妙的比喻(原文见末尾链接):

『   传值:跟copy是一样的。打个比方,我有一橦房子,我给你建筑材料,你建了一个根我的房子一模一样的房子,你在你的房子做什么事都不会影响到我,我在我的房子里做什么事也不会影响到你,彼此独立。

传引用:让我想起了上大学时学习C语言的指针了,感觉差不多。打个比方,我有一橦房子,我给你一把钥匙,我们二个都可以进入这个房子,你在房子做什么都会影响到我。』

先看看两者的定义:

按值传递:函数范围内对值的任何改变在函数外部都会被忽略;仅将对象的值传递给目标对象,说白了就相当于copy;系统将为目标对象重新开辟一个完全相同的内存空间。

按引用传递:函数范围内对值的任何改变在函数外部也能反映出这些修改,也就是说真正的以地址的方式传递参数传递以后,行参和实参都是同一个对象,只是他们名字不同而已对行参的修改将影响实参的值。

1,传值:

<?php
 $param1=1; //定义变量1
 $param2=2; //定义变量2
 $param2 = $param1; //变量1赋值给变量2
 echo $param2; //显示为1
 ?>

2,传引用:

<?php
 $param2=1; //定义变量2
 $param1 = &$param2; //将变量2的引用传给变量1
 echo $param2; //显示为1
 $param1 = 2; //把2赋值给变量1
 echo $param2; //显示为2
 ?>

3,函数传值:

<?php
 //传值
 $param1 = 1; //定义变量1
 function add($param2) //传参数
 {
 $param2=3; //把3赋值给变量2
 }
 $param3=add($param1); //调用方法add,并将变量1传给变量2
 echo '<br>$param1=='.$param1.'<br>'; //显示为$param1==1
 echo '<br>$param2=='.$param2.'<br>'; //显示为$param2== 因为$param2是局部变量,所以不能影响全局
 echo '<br>$param3=='.$param3.'<br>'; //显示为$param3== 因为add方法没有返回值,所以$param3为空
 ?>

4,函数传引用:

<?php
 //传值
 $param1 = 1; //定义变量1
 function add(&$param2) //传参数
 {
 $param2=3; //把3赋值给变量2
 // return $param2; //返回变量2
 }
 echo '<br>$param1=='.$param1.'<br>'; //显示为$param1==1 没对变量1进行操作
 $param3=add($param1); //调用方法add,并将变量1的引用传给变量2
 echo '<br>$param1=='.$param1.'<br>'; //显示为$param1==3 调用变量过程中,$param2的改变影响变量1,虽然没有return
 echo '<br>$param2=='.$param2.'<br>'; //显示为$param2== 因为$param2局部变量,所以不能影响全局
 echo '<br>$param3=='.$param3.'<br>'; //显示为$param3== 如果把方法里面的return注释去掉的话就为$param3==3
 ?>

5,函数传引用2

<?php
//传引用
 $param1 = 1;
 function &add(&$param2)
 {
 $param2 = 2;
 return $param2;
 }
 $param3=&add($param1);
 $param4=add($param1);
 echo '<br>$param3=='.$param3.'<br>'; //显示为$param3==2
 echo '<br>$param4=='.$param4.'<br>'; //显示为$param4==2
 echo '<br>$param1=='.$param1.'<br>'; //显示为$param1==2 调用变量过程中,$param2的改变影响变量1
$param3++;
/*下面显示为$param1==3,这是因为$param2和$param1引用到同一个地方,
 * 返回值前面加了地址符号还是一个引用$param3=&add($param1);
 * 这样$param3,$param2和$param1引用到同一个地方,当$param3++;时,
 * $param1会被改变*/
 echo '<br>$param1=='.$param1.'<br>';
$param4++;
 /* 下面显示为$param1==3,这里为什么是3而不是4呢,这是因为返回值前面没有
 * 地址符号,它不是一个引用所以当$param4改变时不会影响$param1*/
 echo '<br>$param1=='.$param1.'<br>';
 ?>

关于运行速度:

其实在少量数据的情况下其实并没有什么太大的影响,但是数据量很大的情况下按值传递时,php必须复制值。特别是对于大型的字符串和对象来说,这将会是一个代价很大的操作。按引用传递则不需要复制值,对于性能提高很有好处。

参考链接 :

5个php实例,细致说明传值与传引用的区别

案例解析PHP中传值、传引用

PHP传值和传引用、传地址的区别

如何在LEMP服务下安装phpmyadmin

关于phpmydmin

phpmyadmin是一款以PHP为基础,以Web-Base方式架构在网站服务器上的MySQL的数据库管理工具,让管理者可用Web介面管理MySQL数据库。在安装phpmyadmin之前,一定要确保您的服务器上面安装了LEMP 或者是LAMP等服务,这样才能确保phpmyadmin能正常安装以及运行,等安装完WEB服务之后,你就可以开始安装phpmyadmin了。

第一个步骤:安装phpmyadmin

开始使用apt-get来下载并安装它

sudo apt-get install phpmyadmin

在安装过程中,phpmyadmin会问你需不需要配置数据库,选择是。然后还会出现一个选择,就是提示你选择一个服务器:Apache或lighttpd的,随意选择其中一个即可。

第二个步骤:配置phpmyadmin

此时此刻,虽然已经把phpmyadmin安装到服务器当中了,但是,还需要多一部来配置它;phpMyAdmin和您的网站的目录之间需要创建一个链接。如果你正在看前面的步骤,它可能仍位于nginx的默认目录,否则就无法使用:

sudo ln -s /usr/share/phpmyadmin/ /usr/share/nginx/www

重启nginx

sudo service nginx restart

然后就可以访问你的网址:http://xxxxxxxx/phpmyadmin
注意:会有一些意想不到的情况
有可能当你访问了phpmyadmin之后发现,会出现首页空白的情况,不要慌,打开PHP.ini打开错误提示,看看是不是配置错误,若不是配置错误的话可以尝试一下先卸载phpmyadmin,是卸载,不是删除,然后在重新安装一次,期间需要重启nginx服务。

还有需要注意的地方是:第二个步骤,最好是用ln这个命令来做一个链接,不要直接复制phpmyadmin文件夹到nginx的网站目录下。

参考链接:How to Install phpMyAdmin on a LEMP server

如何安装Linux,nginx,Mysql,PHP(LEMP) 于 Ubuntu

关于LEMP

LEMP是一组运行在WEB服务器上面的开源软件,是Linux,nginx(由于英文发音中其实是Engine x,所以首字母为E),MysqlPHP的首字母缩写,由于服务器已经运行Ubuntu,也就是说Linux部分已经安装完成,这里就教大家如何安装剩下的服务。

关于设置

在本教程的步骤中需要用户有ROOT权限。你可以在初始服务器安装教程中的步骤34中看到如何设置。

第一个步骤:Step One—Update Apt-Get

在这个教程当中,我们将会使用apt-get来进行服务器程序安装,在2012年的58PHP被发现严重的漏洞,重要的是我们需要下载最新的补丁来保护我们的服务器。

让我们来做一次更新吧:

sudo apt-get update

第二个步骤:安装Mysql

Mysql是一个强大的用于组织和管理检索数据的数据库管理系统。

安装先打开终端,输入这些命令:

sudo apt-get install mysql-server libapache2-mod-auth-mysql php5-mysql

在安装的过程中,Mysql会要求你设置root密码。如果你在安装的时候错过来设置密码的机会,没关系,在安装完成后也是非常容易设置的。

一旦你安装来Mysql,我们要用这个命令激活它:

sudo mysql_install_db

最后通过运行MySQL建立脚本:

sudo /usr/bin/mysql_secure_installation

然后会提示你输入当前的跟密码,然后输入它

Enter current password for root (enter for none): 
OK, successfully used password, moving on...

然后提示会问你,如果你想更改根密码,选择“N”到下一个步骤

这是最简单的应答所有的选项,最后,将会重新加载和执行Mysql的新变化

By default, a MySQL installation has an anonymous user, allowing anyone

to log into MySQL without having to have a user account created for
them.  This is intended only for testing, and to make the installation
go a bit smoother.  You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] y                                            
 ... Success!

Normally, root should only be allowed to connect from 'localhost'.  This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] y
... Success!

By default, MySQL comes with a database named 'test' that anyone can
access.  This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] y
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] y
 ... Success!

Cleaning up...

完成了之后你就可以完成安装“PHP”

第三个步骤:安装NGINX

一旦安装好来Mysql,我们就可以在VPS上面安装NGINX

sudo apt-get install nginx

nginx不是自动启动的,想要让nginx运行,输入命令

sudo service nginx start

您可以确认您的WEB服务器上面已经安装了nginx,您可以通过浏览器输入您的IP地址,你可以运行以下命令来显示您的VPSIP地址。(若在本地安装此步骤可以酌情省下)

ifconfig eth0 | grep inet | awk '{ print $2 }'

第四个步骤:安装PHP

安装PHP,先要打开终端,输入以下命令,下一步,我们将设置nginxphp的详细配置

sudo apt-get install php5-fpm

第五个步骤:配置PHP

我们需要对PHP配置文件做一点小小的改动,打开php.ini

sudo nano /etc/php5/fpm/php.ini

找到这一行:“ cgi.fix_pathinfo=1” a并且把1 改为 0

cgi.fix_pathinfo=0

如果这个数字保持为“1”,那么,PHP解释器就会尽其所能的处理文件尽可能靠近可能请求的文件。这是一个可能的安全风险。如果这个数字为“0”,相反,解释器只会处理精确的文件路径并且更加安全。保存并且退出。

我们需要对PHP5- fpm配置文件做一点小小的改动,打开www.conf

sudo nano /etc/php5/fpm/pool.d/www.conf

找到这一行“ listen = 127.0.0.1:9000,”a然后把“127.0.0.1:9000”改为“ /var/run/php5-fpm.sock“

listen = /var/run/php5-fpm.sock

保存并且退出。

重启php-fpm:

sudo service php5-fpm restart

第六个步骤:配置nginx

打开默认的虚拟主机文件

sudo nano /etc/nginx/sites-available/default

配置的变化应包括如下(细节上的变化是根据配置信息):

更新:Ubuntu的新版本默认创建一个称为‘HTML’,而不是‘www’的默认目录。如果/ usr /share/ nginx/ WWW不存在,它可能称为HTML。请确保您更新您的配置是否正确(一致即可)。

 [...]

server {
        listen   80;

        root /usr/share/nginx/www;
        index index.php index.html index.htm;

        server_name example.com;

        location / {
                try_files $uri $uri/ /index.html;
        }

        error_page 404 /404.html;

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
              root /usr/share/nginx/www;
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ \.php$ {
                #fastcgi_pass 127.0.0.1:9000;
                # With php5-fpm:
                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;

        }

}
[...]

这里有一些细节的变化:

添加index.php文件到索引行。

从本地主机服务器名更改你的域名或IP地址(在配置替换example.com

更改争取的行“location ~ \.php$ {“ 的一部分

保存然后推出

第七个步骤:创建一个PHP详细信息页

我们可以很快看到新的PHP配置的细节。
要对此进行设置,首先创建一个新的文件:

sudo nano /usr/share/nginx/www/info.php

键入以下代码在文件中:

<php? phpinfo(); ?>

保存然后退出,重启NGINX

sudo service nginx restart

现在你可以看到nginxPHP-FPM配置的详细信息,请访问http://youripaddress/info.php

现在您的LEMP服务已经配置到你呢的服务器上面了。

关于更多

安装完 LEMP之后, 您可以安装 WordPress,继续做更多的MySQL (一个基本的MySQL 教程) 或者安装 phpMyAdmin, 创建一个 SSL Certificate,证书,或者安装一个FTP Serverftp服务器.

参考文章:How to Install Linux, nginx, MySQL, PHP (LEMP) stack on Ubuntu 12.04

CI框架实现基本的文件上传

接着CI框架实现基本的数据库操作这一篇的说,这些其实初学者都很容易弄懂的,因为数据库就是什么增删添改。

所以这一篇来说说上一篇被注释了的部分:文件上传!

这一回先说视图层好了:

在上一篇刚才的qa.php文件里面的代码:

    <form action="insert" method="post" enctype="multipart/form-data">
         标题:<input type="text" name="question" /><br />
         内容:<input type="text" name="answer" /><br />
         图片:<input type="file" name="image" /><br />
         <input type="submit" value="提交" />
    </form>

这个表单一定要记者enctype=”multipart/form-data”这个东东,没了它一定无法上传,切记切记;

然后是修改页面的视图代码:

	<form action="<?php echo "updata"?>" method="post" enctype="multipart/form-data">
		 标题:<input type="text" name="question" /><br />
		 内容:<input type="text" name="answer" /><br />
		 图片:<input type="file" name="image" /><br />
		 <input type="submit" value="提交" />
		 <input type="text" name="id" value="<?php echo "$id"?>" />
	</form>

注意action

然后就是在上一篇的控制器feadmin.php加上文件上传类:

	//文件上传
	function fileUp(){
		$config['upload_path'] = './upload/img';
		$config['allowed_types'] = 'gif|jpg|png';//文件类型
		$config['max_size'] = '0';
		$config['encrypt_name'] = true;
		$this->load->library('upload',$config);
		if ($this->upload->do_upload('image')) {
			$upload_data = $this->upload->data();
			return $upload_data['file_name'];
		}
	}

就是这个它可以实现文件上传,

在插入的时候代码

	//插入
	function insert() {
		$_POST['image'] = $this->fileUp();
		$data=array(
				'question'=>$_POST['question'],
				'answer'=>$_POST['answer'],
				'img'=>$_POST['image'],
		);
		$this->fe_model->insert($data);
		redirect('feadmin/look','refresh');
	}

注意开头的那句$this->fileUp();它就是用那个文件上传类,搞定你要传过来的图片。

然后就是有修改了:是修改,不是修改查询

	//修改
	function updata(){
		$_POST['image'] = $this->fileUp();

		$id = $this->input->post('id');
		$this->db->where('id',$id);
		$query= $this->db->get('yanzhengma');
		$row = $query->row_array();
		if($row['img'] != "")
		{
			$file_path = 'upload/img/'.$row['img'];//获取路径
			unlink($file_path);//删除图片
		}
		$_POST['image'] = $this->fileUp();
		$sel = array(
				'question'=>$_POST['question'],
				'answer'=>$_POST['answer'],
				'img'=>$_POST['image'],
		);
		if(!empty($id) && (count($sel)>1)){///判断id值是否传过来并且判断封装的数组是否有元素存在
			$this->db->where('id',$id);
			$this->db->update('yanzhengma',$sel);
		};
		redirect('feadmin/look','refresh');
	}

依旧是加上了一个unlink()删掉之前的文件。

然后就是和插入一个道理,只不过MYSQL语句换为UPDATE罢了。

代码什么的,戳这里下载,两篇文章可能写的很粗浅,代码也都很简单,但是必须从简单开始,省的一口吃个大胖子出错多。

 

CI框架实现基本的数据库操作

凡是学什么东西就要从头学起,哪怕是一个小小的功能也必须要写出来,不要怕别人看见,即使写的很臃肿,或者是很难看,也没关系,这些都是你成长必须的,这些日子重新捡起来CI框架了,这个是比较适合初学者学习的框架,因为它的分层非常的明确,我的一个好基友用的是Yii,他对CI也非常熟悉,他说Yii基本都可以自己生成一些东西,没必要那么麻烦;其实吧,框架乃至于任何代码都是一种工具,至于如何使用,就看你自己的技巧人品和造化。

其实关于模型和控制器,看你如何写,如何用,你也可以全部都在模型里面把数据全部读取,也可以只在其中一个,我们主管说,反正三层呢,能少用一层是一层,于是乎:

控制器来了,在controllers文件夹下新建feadmin.php:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

这个东东是必须的,如果没有的话,会出现一些莫名其妙的问题;然后接下来:

class Feadmin extends CI_Controller{
	function __construct(){
		parent::__construct();
		$this->load->database();
		$this->load->model('fe_model');
		$this->load->helper('url');
	}

身为一个初学者的话一定要多看手册,关于类名和文件名必须相同什么的这样的超级低级错误,一定要小心;然后就是查看:

//查看
	public function look(){
		$query = $this->fe_model->get();
		$get['qalist'] = $query->result_array();
		$this->load->view('qa',$get);
	}

手册里面说了不推荐驼峰命名法,于是下划线就变成一个很好用的东西;接下来插入:

	//插入
	function insert() {
		//$_POST['image'] = $this->fileUp();
		echo $_POST['question'];
		echo $_POST['image'];
		$data=array(
				'question'=>$_POST['question'],
				'answer'=>$_POST['answer'],
				'img'=>$_POST['image'],
		);
		$this->fe_model->insert($data);
		redirect('feadmin/look','refresh');
	}

里面有一个FileUP()函数,是一个文件上传类,所以这句话可以不要先,然后是删除:

//删除
	function delete(){
		$id = $this->input->get('id');
		$this->db->where('id',$id);
		$query= $this->db->get('yanzhengma');
		$row = $query->row_array();
		//echo $row['img'];
		//if($row['img'] != "")
		//{
		//	$file_path = 'upload/img/'.$row['img'];//获取路径
		//	unlink($file_path);//删除图片
		//}
		$this->db->where('id',$id);
		$this->db->delete('yanzhengma');
		redirect('feadmin/look','refresh');
	}

上面我注释掉的那几行代码呢,在下一篇文章会说到,关于简单的文件上传什么的。接下来是查询修改,注意是查询修改哦,不是修改,就是只是把你要修改的数据查询出来

//查询修改
	function select_updata(){
		$sel['id'] = $this->input->get('id');
		$this->load->view('qa_up',$sel);
	}

这里才是真正的修改:

	//修改
	function updata(){
		$_POST['image'] = $this->fileUp();
		$id = $this->input->post('id');
		$this->db->where('id',$id);
		$query= $this->db->get('yanzhengma');
		$row = $query->row_array();
		//if($row['img'] != "")
		//{
		//	$file_path = 'upload/img/'.$row['img'];//获取路径
		//	unlink($file_path);//删除图片
		//}
		//echo $_POST['image'];
		$_POST['image'] = $this->fileUp();
		//echo $_POST['image'];
		$sel = array(
				'question'=>$_POST['question'],
				'answer'=>$_POST['answer'],
				'img'=>$_POST['image'],
		);
		if(!empty($id) && (count($sel)>1)){///判断id值是否传过来并且判断封装的数组是否有元素存在
			$this->db->where('id',$id);
			$this->db->update('yanzhengma',$sel);
		};
		redirect('feadmin/look','refresh');
	}

依旧照例的呢,上面注释的那部分内容是用来删除图片什么的,如果木有图片什么的,可以无视掉。
是不是要到模型了?没关系,这个暂时没有用到模型,其实把上面的代码里面的DB的部分换到模型,其实一样可以用。
终于到视图层了:在view文件夹下新建qa.php:

<body>
	<table > 
	<?php foreach ($qalist as $i): ?>
		<tr  style="text-align:left;" >  
			<td><?php echo $i['id'] ;?></td> 
			<td><?php echo $i['img'] ;?></td> 
			<td><?php echo $i['question'] ;?></td> 
			<td><?php echo $i['answer'] ;?></td>
			<td>
				<a href="<?php echo "select_updata" ?>?id=<?php echo $i['id']?>">修改</a> |
                <a href="<?php echo "delete" ?>?id=<?php echo $i['id']?>">删除</a>
			</td> 
	<?php endforeach; ?>

		<table>

	<form action="insert" method="post" enctype="multipart/form-data">
		 标题:<input type="text" name="question" /><br />
		 内容:<input type="text" name="answer" /><br />
		 图片:<input type="file" name="image" /><br />
		 <input type="submit" value="提交" />
	</form>
</body>

恩,没错,添加的表单和查看的,都在这里。
然后是修改的视图,在view文件夹下新建qa_up.php

<body>
	<form action="<?php echo "updata"?>" method="post" enctype="multipart/form-data">
		 标题:<input type="text" name="question" /><br />
		 内容:<input type="text" name="answer" /><br />
		 图片:<input type="file" name="image" /><br />
		 <input type="submit" value="提交" />
		 <input type="text" name="id" value="<?php echo "$id"?>" />
	</form>
</body>

然后就可以输入http://localhost/test/index.php/feadmin/look 自己去闹一闹了。
待会下一篇把文件上传的东东和代码补上去。

解决方案的理念

之前做的一个项目,需要一个解决方案,大致上就是要无缝加载和切换页面,怎么才能最快速的加载完成页面,最好是要做到零延迟,或者是说感官上的零延迟,着实费了一番头脑来想。所以最终的解决方案是使用JS的DOM事件对象来直接替换部分需要重写的代码即可,说白一点,就是所有页面全部写在同一个文件里面,同样,页面所有的元素都只需要加载一次即可,也包括类似于比较大的图片之类的文件,也就是说第一次加载完成,后续的直接用JS调用替换就行,也就相当于在本地执行,所以算是解决了这些个方法。

最开始在想需求的时候设想是需要加载很多页面的,但没想到的是,在硬件状况不理想的情况下,会有如此之大的差别,这也告诫我在以后的项目当中需要注意的事情,在预先我还没有想到的情况下,不能盲目动手;而且,在提高单个页面的加载速度的话,有一些思路可以,一种是划分,也就是分割成非常多非常多的同时加载,还有就是在一开头全部加载完成,需要一个稍微长时间的进度条,随后加载完成之后就可以实现无缝加载;显然项目需要的是后者。

所以具体到这个项目的实施来看,要用到大量的JS,首先,需要一个规定成为动作(可以命名为Action)的JS文档,它包括了你需要使用的一些JS操作的基本,比如,写入,更改样式,更改文本,显示,隐藏,加载,等等一些动作;然后第二个JS文件,定义了哪些文件需要的单个操作(Dom),比如,我需要隐藏的某个DIV,或者是某个模块,还有也要把显示的内容放进去,比如基础文本,比如模块的样式;第三个文件就是属于调用了,就是大量的ID穿梭的文件把内容填充进去(Content);这样把三个需要绕来绕去的都剥离出来,可扩展性就很强了,也算是体现了面向对象的一点点点点的小思想。

所以思想要咸鱼行动,解决一件事情的时候,先别动手,想好思路在开动,要不然会在后面一定会需要大量的的Bug与Debug循环。