数据结构 – 反转链表

假如我们已经构造了如下链表:

头结点->6->5->4->3->2->1->null

现在要求反转链表,使其变成:

头结点->1->2->3->4->5->6->null

思路分析:既然是反过来,那么就从反过来后的第一个元素入手,即null,让第一个结点的next先指向null,类似: 6->null这个结构,继续这个思路,那下一步就是让5指向刚刚这个结构,依次类推,知道最后一个1指向2->3->4->5->6->null

代码:

// 2. 链表反转: 假设有链表6->5->4->3->2->1,反转链表成为1->2->3->4->5->6
function reverse(LineStruct $linelist) {
	// 第一个结点
	$pre = null;
	$leftNodes = $linelist->next;	
	while($leftNodes) {
		① $temp = $leftNodes->next;
		② $leftNodes->next = $pre;
		③ $pre = $leftNodes;
		④ $leftNodes = $temp;
	}
	$headNode = new LineStruct('',$pre);
	return $headNode;
}

先从第一遍开始分析,$leftNodes为6->5->4->3->2->1->null,之所以叫leftNodes,抽象表示剩下的结点,① 我们先用$temp保存6的下一个结点,即5->4->3->2->1->null,② 然后让6指向预先定义的pre,第一个当然是null,这个pre后面会改变。得到的结构是6->null,③ 接着我们用pre来保存这个结构,最后也是最重要的一部步,④将$leftNodes用$temp的值替换,那么下次循环就是从5这个结点开始了,先保存剩下的结点,,即4->3->2->1->null,然后5会先指向刚刚的pre,即6->null,变成5->6->null,这个结构一会再保存为pre,然后再讲$leftNodes置为$temp,直到最后一个$leftNodes = null,跳出循环,这时候的$pre就是1->2->3->4->5->6->null。

所以整个思路里最重要的就是保存当前结点的下一个结点,即保存剩下的结点。

 

不要再用传统分页

原文地址:http://blog.csdn.net/yejr/article/details/70039403

在开发项目的过程中随时可能遇到深度分页的情况,比如:

select * from table order by id desc limit 200000,10;

如果explain这个语句,结果是这样

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE yd_stock ALL NULL NULL NULL NULL 233355 Using filesort
 没有使用任何的索引,全表扫描了20万数据。这个就会使查询的效率下降,针对这种情况可以优化。
 推荐使用inner join,比如将sql改成

select * from table a inner join (select id from table order by id desc limit 200000,10) b using(id)

这个sql的执行耗时比上一个提高了几十倍。explain后面的子句

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE yd_stock index NULL PRIMARY 3 NULL 200010 Using index
 使用了主键索引,所以这个查询会很快,自然整个语句的执行速度就上去了

php中的socket一点总结

socket俗称套接字,是网络通信编程的核心。php开发中也会用到,关于socket在php中的应用,做一点总结。

socket在TCP/IP协议中,属于应用层和传输层中间,相当于一层接口,将复杂的协议内容藏在后面。

既然socket用于网络通信,那么就以聊天室为例子:

首先搭建一个聊天室,我们需要一个服务端,作为一个平台,用于让用户连接,我们可以用socket搭建一个服务端。

先贴上代码

<?php
/*
   聊天室服务器端
 */

/* 防止连接超时 */
set_time_limit(0);

$host = "127.0.0.1";
$port = 8888;
$reads = [];
$write = $except = null;

// 建立socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!is_resource($socket)) die("init socket fail");

// 可以重复利用本地ip
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

// 绑定地址和端口
socket_bind($socket, $host, $port);
// backlog队列中最多有十个排队等待处理的链接
socket_listen($socket, 10);

$reads[] = $socket;
while(true){
	// 最初有一个服务器端监听的socket
	// 当数组中的某个有活动时,才会返回
	// 返回时剔除了$read里不可操作的套接字
	// 并不是服务端监听的套接字会变成可读,用户连接的套接字也会变成可读
	// 如果是服务器监听套接字有活动,创建一个新的socket用于处理用户的请求
	// 如果是用户连接套接字有活动,处理用户消息
	$readUps = $reads;
	$return = @socket_select($readUps, $write, $except, NULL);
	if($return === false) {
		die("Failed to listen for clients: ".socket_strerror(socket_last_error()));
	}

	foreach($readUps as $k => $_socket){
		// 有活动的是服务器监听的套接字
		if($_socket === $socket){
			// 相当于用户连接建立的套接字,用于处理用户传来的数据
			$connect = socket_accept($socket);
			$reads[] = $connect;
			continue;
		}
		// 获取用户消息
		// $data为引用传递
		if(!socket_recv($_socket, $data, 1024, 0) || !$data){
			socket_close($_socket);
			continue;
		}
		echo $data;die;
		// preg_match("#Sec-WebSocket-Key: (.*?)\r\n#", $buffer, $match) && $key = $match[1];

		// $key .= "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
		// $key = sha1($key);
		// $key = pack('H*', $key);
		// $key = base64_encode($key);

		// $upgrade =
		//         "HTTP/1.1 101 Switching Protocols\r\n" .
		//         "Upgrade: websocket\r\n" .
		//         "Connection: Upgrade\r\n" .
		//         "Sec-WebSocket-Accept: {$key}\r\n\r\n";
	}
	sleep(1);
}

这段代码中主要一个是socket_select还有一个就是socket_accept

首先socket_select:

socket_select 接受三个套接字数组,分别检查数组中的套接字是否处于可以操作的状态(返回时只保留可操作的套接字)
使用最多的是 $read,因此以读为例
在套接字数组 $read 中最初应保有一个服务端监听套接字
每当该套接字可读时,就表示有一个用户发起了连接。此时你需要对该连接创建一个套接字,并加入到 $read 数组中
当然,并不只是服务端监听的套接字会变成可读的,用户套接字也会变成可读的,此时你就可以读取用户发来的数据了
socket_select 只在套接字数组发生了变化时才返回。也就是说,一旦执行到 socket_select 的下一条语句,则必有一个套接字是需要你操作的

再来是socket_accept:

此函数接受唯一参数,即前面socket_create创建的socket文件(句柄)。返回一个新的资源,或者FALSE。本函数将会通知socket_listen(),将会传入一个连接的socket资源。一旦成功建立socket连接,将会返回一个新的socket资源,用于通信。如果有多个socket在队列中,那么将会先处理第一个。关键就是这里:如果没有socket连接,那么本函数将会等待,直到有新socket进来。

其实就是,如果有一个客户端连接服务器了,那么就创建一个新的socket用于跟服务器端交流

如果前面不用socket_select在没有socket的时候阻塞住程序,那么就卡在这里永远无法结束了。

服务器端代码好了,下面就是客户端,代码如下:

<?php
/*
   聊天室客户端
 */

/* 防止连接超时 */
set_time_limit(0);

$host = "127.0.0.1";
$port = 8888;

// 建立socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!is_resource($socket)) die("init socket fail");

echo "trying connect $host on port : $port".PHP_EOL;
if(!socket_connect($socket, $host,$port)) die("connenct fail");
echo "success!";
 
socket_write($socket, "hello", 5);

socket_close($socket);

我们连接服务端,然后发送了一个hello,窗口下已经能看到输出了

这里有个地方需要注意,如果我们在服务端的代码这块加上输出

$return = @socket_select($readUps, $write, $except, NULL);var_dump($readUps);

你会发现有两个输出,而且是不一样的socket,?????

因为client连接server时,socket_select检测到服务端监听的socket有活动,所有socket_select返回了,代码往下执行了,接着socket_accept新建了一个socket,紧接着client又socket_write,向server发送信息,socket_select检测到刚刚accept新建的socket(也就是客户端过来的连接)有活动,于是socket_select再次返回,于是代码继续执行。所以会输出两次。

php命名空间的一点总结

现在很多框架都是采用mvc模式,所以大部分框架都有命名空间的概念,比如

<?php
namespace test;
use core\control;
class testControl extends control{}

这样做主要是为了区分文件,而不至于出现载入两个相同名称类等问题,在开发中经常时间长了就容易混淆,所以这里做个总结。

  1. use关键字不等于自动加载,其实框架里很多地方使用use,便可以使用对应的类,不是因为use有加载的功能,而是框架实现了__autoload这个魔术方法。

2. use会自动起别名,默认是类名。

3. 文件如果有namespace关键字,那么整个文件便处于对应的域下,在使用定义的类时要带上空间名,这里经常会出问题。

下面举个例子说明:

同一目录下有1.php和2.php两个文件,如下

1.php:

<?php
namespace one;
class one{
	static public function index(){
		echo "helloworld";
	}
}

2.php

<?php
namespace two;
use one\one;

class two{
	public function __construct(){
		one::index();
	}

	static public function dl($className){
		if($className == "one\one") require "1.php";
	}
}
spl_autoload_register(array('two','dl'));
$two = new two(); 

一运行会报错


Fatal error: Uncaught exception 'LogicException' with message 'Passed array does not specify an existing static method (class 'two' not found)' in D:\Xampp\htdocs\2.php:14 Stack trace: #0 D:\Xampp\htdocs\2.php(14): spl_autoload_register(Array) #1 {main} thrown in D:\Xampp\htdocs\2.php on line 14

提示找不到two这个类,很显然,因为two类是在two这个命名空间类,所以应该改成

spl_autoload_register(array('two\two','dl'));

这个类似于目录的绝对路径,前面其实还有个\符号,但是一般都省略,但是如果不use而是直接调用,那么这个\就不能省略。

但是为什么

one::index()

这样写不报错,照理说应该是会理解成two\one才对,其实use会自动起别名,

use one\one;

等同于

use one\one as one;

所以上面的写法会理解成

one\one::index()

 

 

 

ElasticSearch接触后的一点心得

最近项目需要使用elasticsearch来加速数据的搜索,上网找了一下教程,发现不是那么简单。光安装这一块就费了很大功夫。

中文文档安装head插件文档索引时报错

首先在windows下安装elasticsearch,必须的条件就是java环境,然后去官网下载压缩包解压,配置环境变量,然后执行elasticsearch.bat,访问http://localhost:9200测试是否启动。

启动的时候可能会报错,大概是java虚拟环境的内存分配不合理。如图

更改config/jvm.options,增加

-Xms512m
-Xmx1g

 

然后是head插件的安装,5.*以上版本bin/elasticseach-plugin.bat没法直接在命令行下使用。只能去github上clone项目后,再自行安装。

安装后访问,http://localhost:9100测试,这儿可能会提示集群未连接,这是因为浏览器限制了跨域,打开config/elasticsearch.yml,增加以下两句接即可。

 http.cors.enabled: true
 http.cors.allow-origin: "*"

一切就绪开始使用时,又出现问题,为某个文档索引时,命令如下:

curl -X PUT ""http://localhost:9200/megacorp/employee/1?pretty" -d "{"name":"laokiea","age":20}"

执行就会报错failed_to_parse,应该是解析json的时候出错。查询得知,windows使用双引号,需要加\,类似于转义。

elasticsearch有php的api,研究了下,可以很轻松的建立索引,索引文档等,但是需要composer依赖。下载安装composer,编写composer.json

{
    "require": {
        "elasticsearch/elasticsearch": "~5.0"
    }
}

然后composer install命令,即可在项目文件夹下创建vendor目录

举例针对一个产品表,10万条数据,全部索引,代码(用的TP框架)如下:


require "././././vendor/autoload.php";
public function createIndexAndBatchIndex()
    {
    	$params['index'] = "sys_config";
    	$client = \Elasticsearch\ClientBuilder::create()->build();
    	$client->indices()->delete($params);
    	$client->indices()->create($params);

    	//索引文档
    	$Model = M("ranzhi_100001.product","yd_");
    	$total = $Model->field(["count(*)"=>"total"])->find()["total"];
    	for($i=0;$i<ceil($total/5000);$i++){
    		$begin = 5000*$i;
    		$end = 5000*($i+1);
    		$products = $Model->field(["id","sname","name"])->limit($begin,$end)->select();
    		$documents = ['body'=> []];
    		$j = 1;
    		foreach ($products as $product) {
    			$documents['body'][] = ["index" => ["_index" => "sys_config", "_type" => 'config', "_id" => $begin+$j]];
    			$documents['body'][] = ["sname"=>$product['sname'],"name" => $product['name']];
    			$j++;
    		}
    		$client->bulk($documents);
    		unset($documents);
    	}
    	echo "all has been indexed";
    }

结果如图

100000+数据全部被索引。


03.01 总结:

通常项目里使用elasticsearch,做法是,拷贝需要搜索的数据到elasticsearch,然后用其做搜索查询,但是可能会有 并发操作的问题,举个例子,比如有100件商品,A,B两个人同时去卖一个商品,

那可能就会造成A,B 都以为只减少了一个产品, 但实际上却减少了两个。这有点类似数据库的锁问题。

这里可以有两个方法解决:

第一种,对文档的操作通常会有版本号,比如创建一个文档返回的结果里,_version是1,然后再去执行一次,这次已经变成更新了,_version也会随之增加成2,如果这时候我们再去访问一次但是在地址后面带上?version=1,表示说只希望对version为1的文档进行操作。执行就会返回409错误。再回到上面的问题,A或B在做更新时,先取得最新的文档信息,然后再请求时附带上信息里的_version信息,如果有别人已经修改过,那么_version信息也会被更改,那么此次请求肯定是不成功的。

第二种,还是类似上面的做法用版本号去控制,只不过不是使用系统分配的版本号,而是使用自定义的,比如你的系统里有某些信息(时间戳等)可以充当。自定义的版本号必须是正数且大于0,请求时在查询字符串后面带上?version=your version&version_type=external。


3.6总结:

今天开始尝试编写代码。首先第一个问题就是分词,elasticsearch自带好几种分词器,对中文也有chinese分词,但是效果不是很好,所以采用ik分词器。对中文分词实现的很好。

先去github上clone项目。再利用maven打包,mvn -package,打包成jar文件,放入es目录下的plugins下,重启es,报错,提示ik版本与es版本不符合,于是改成直接下载tar压缩包,地址:https://github.com/medcl/elasticsearch-analysis-ik/releases,选择对应版本,解压后直接放入es目录下的plugins/ik,无需打包。

然后测试:

curl -X GET "http://localhost:9200/_analyze?analyzer=ik_max_word&text=你好啊&pretty"

出现乱码并没有按照中文分词,原因是windows下curl默认使用的编码是gbk。需要接受utf-8的编码。于是写一段代码转成%xx的格式:

<?php
$str = "你好啊";
$utf = '';
$str = iconv($str, "GB2312","UTF-8");
for($i=0;$i<strlen($str);$i++){
// %表示转义
$utf .= sprintf("%%%02X",ord(substr($str,$i,1)));
}

再测试成功,说明中文分词可以。


3.7总结

放上索引数据的脚本:

 #!/usr/bin/env php
<?php

$v_autoload =  "../../vendor/autoload.php";
require $v_autoload;

// 以下都是测试数据
$user = "root";
$pass = "";
$dbname = "ranzhi_100001";
$host = "127.0.0.1";
$batchNum = 5000;
$es_hosts = ["localhost:9200"];

$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8mb4",
];
try{$pdo = new PDO("mysql:dbname=".$dbname.";host=".$host,$user,$pass,$options);}
catch(PDOException $e){
     die("PDO init fail : ".$e->getMessage());
}

$countSql = "SELECT count(*) as count from yd_stock";
$count = $pdo->query($countSql)->fetch(PDO::FETCH_OBJ)->count;

try{$client = \Elasticsearch\ClientBuilder::create()->setHosts($es_hosts)->build();}
catch(Exception $e){
     die("Elasticsearch init fail : ".$e->getMessage());
}
// 为库存产品相关产品建立索引
$index = [
     "index" => '1pei',
     "body" => [
          "settings" => [
               "number_of_shards" => 5,   // 索引数据分配到三个主分片上
               "number_of_replicas" => 2, // 每一个主分片都有两个复制分片
          ],
          "mappings" => [
               // 对stockProduct类型映射,(只对可能用于搜索的属性索引)
               "stockProduct" => [
                    // 查询到的文档主体以json格式保存在_source字段中
                    "_source" => ["enabled" => true],

                    // 此选项表示在该类型下索引文档,id默认从sid这个字段中生成
                    "_id" => ["path" => "sid"],

                    // 搜索禁用_all,如果启用,类似一个独立的字段,分词器采用ik
                    "_all" => [
                         "enabled" => false,
                         "analyzer" => 'ik_max_word',
                    ],
                    // 此设置表示如果该类型增加了新的字段,会抛出一个异常,但是可以在内部对象中设置成true
                    "dynamic" => "strict",

                    // 建立一个动态模板用于查询相同类型下的相同字段,但是类型不同的情况
                    "dynamic_templates" => [
                         "es" => [
                              "match" => "*_es",
                              // 规定匹配的字段只能是string类型
                              "match_mapping_type" => "string",
                              "mapping" => [
                                   "type" => "string",
                                   "index" => "analyzed",
                                   "analyzer" => "ik_max_word",
                              ],
                         ],
                         "es_path" => [
                              // 用于内部对象
                              "path_match" => "info.name",
                              "match_mappping_type" => "string",
                              "mapping" => [
                                   "type" => "string",
                                   "index" => "analyzed",
                                   "analyzer" => "ik_max_word",
                              ],
                         ],
                    ],

                    "properties" => [
                         "sid" => ["type" => "long"],
                         "pid" => ["type" => "long"],
                         "partid" => ["type" => "long"],
                         "productName" => ["type" => "text","index" => "analyzed","analyzer" => 'ik_max_word'],
                         "sname" => ["type" => "text","index" => "analyzed","analyzer" => 'ik_max_word'],
                         "pn" => ["type" => "text"],
                         "oem" => ["type" => "text"],
                         "manufacturer" => ["type" => "text","index" => "analyzed","analyzer" => 'ik_max_word'],
                         "madein" => ["type" => "text","index" => "analyzed","analyzer" => 'ik_max_word'],
                         "dwgno" => ["type" => "text"],
                         "materialNumber" => ["type" => "text"],
                         "inStockNo" => ["type" => "text"],
                         "standard" => ["type" => "text","index" => "analyzed","analyzer" => 'ik_max_word'],
                         "unit" => ["type" => "text","index" => "analyzed","analyzer" => 'ik_max_word'],
                         "store" => ["type" => "long"],
                         "shelf" => ["type" => "long"],
                         "count" => ["type" => "long"],
                         "dynacount" => ["type" => "long"],
                         "chainID" => ["type" => "long"],
                         "provider" => ["type" => "long"],
                         "providerName" => ["type" => "text","index" => "analyzed","analyzer" => 'ik_max_word'],
                         "storeName" => ["type" => "text","index" => "analyzed","analyzer" => 'ik_max_word'],
                         "shelfName" => ["type" => "text","index" => "analyzed","analyzer" => 'ik_max_word'],
                         "partGroupName" => ["type" => "text","index" => "analyzed","analyzer" => 'ik_max_word'],
                    ],
               ],
          ],
     ],
];
try{if($client->indices()->getSettings(["index"=>"1pei"])) goto _index;}
catch(Exception $e){}
$client->indices()->create($index);

_index:
// 索引数据
$allDataSql = "SELECT t4.name as partGroupName, s.id as sid, p.id as pid, p.partid, p.name as productName, p.sname,p.pn,p.oem, p.manufacturer, p.madein, p.dwgno, p.materialNumber, p.standard, p.unit,s.store, store.name as storeName,shelf.name as shelfName,s.shelf,s.count,s.dynacount, s.inStockNo, s.provider,c.name as providerName,s.chainID FROM yd_stock s LEFT JOIN yd_product p on s.pid = p.id LEFT JOIN ranzhico.scm_parts t3 on p.partid = t3.id LEFT JOIN ranzhico.scm_parts t4 on t4.id = t3.parent LEFT JOIN crm_customer c on s.provider = c.id LEFT JOIN yd_store store on s.store = store.id LEFT JOIN yd_shelf shelf on shelf.id = s.shelf RIGHT JOIN (SELECT id FROM yd_stock limit %d,%d) b on b.id = s.id";
$properties = array_keys($index['body']['mappings']['stockProduct']['properties']);

for($i=0;$i < ceil($count/$batchNum); $i++){
     $beginTime = time();
     $begin = $batchNum * $i;
     $end = $batchNum * ($i+1);
     $stmt = $pdo->prepare(sprintf($allDataSql, $begin, $batchNum));
     $stmt->execute();
     $result =  $stmt->fetchAll(PDO::FETCH_OBJ);
     $documents = ['body'=> []];
     foreach ($result as $id => $stock) {
          $documents['body'][] = ["index" => ["_index" => "1pei", "_type" => 'stockProduct', "_id" => $stock->sid,"timestamp" => time(),]];
          foreach($properties as $property){
               $document[$property] = $stock->$property;
          }
          $documents['body'][] = $document;
     }
     $client->bulk($documents);
     unset($documents,$result);
     $currentMemory = memory_get_usage()/1024/2024;
     echo "[$i] time success from [$begin] to [$end], spent [".(time()-$beginTime)."]s, current memory is [".$currentMemory."M]".PHP_EOL;
}
echo "data indexed complately".PHP_EOL;

 

测试脚本:,成功索引。

查询数据:,中文分词可以使用。

研究了5.0接口文档,其中一个future(并发)用法值得尝试一下,如下

$params = [
		    'index' => '1pei',
		    'type' => 'stockProduct',
		    'id' => 433138,
		    'client' => [
		        'future' => 'lazy'
		    ]
		];
$future = $client->get($params);
// 此时返回的不是相应体,而是一个future对象

可以同时多个请求,并在一个数组内将所有的结果返回。


3.8总结

继续研究php api,最后给了一个插件用来转换curl和php客户端的dsl,挺方便的,依赖composer,执行命令安装

composer require ongr\elasticsearch-dsl

比如需要以下查询


        $boolQuery = new \ONGR\ElasticsearchDSL\Query\Compound\BoolQuery();
        $geoQuery = new \ONGR\ElasticsearchDSL\Query\TermLevel\termQuery("store",2);
        $matchQuery = new \ONGR\ElasticsearchDSL\Query\FullText\matchQuery("sname","离合器");
        $boolQuery->add($geoQuery,\ONGR\ElasticsearchDSL\Query\Compound\BoolQuery::FILTER);
        $boolQuery->add($matchQuery, \ONGR\ElasticsearchDSL\Query\Compound\BoolQuery::MUST);
      
        $search = new \ONGR\ElasticsearchDSL\Search();
        $search->addQuery($boolQuery);
      
    	$params = [
    		"index" => "1pei",
    		"type" => "stockProduct",
    		"body" => $search->toArray(),
    		// "client" => [
    		// 	"verbose" => true,
    		// ]
    	];

构造的结果类似

{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "sname" : "离合器",
                    }
                }
            ],
            "filter": [
                {
                    "term": {
                        "store": "2",
                    }
                }
            ]
        }
    }
}
省去了自己编写dsl的麻烦,还容易出错。

每日总结:正则替换,PHP://

1. 正则
<?php
$a = "<a href='javascript:void(0)'>XS1000010011702130002</a>";

$o = "hello";

echo preg_replace_callback("/\<a.*?\>(.*)\<\/a\>/", function($matches){return str_replace($matches[1],$o,$matches[0]);}, $a);

上面这段代码,是想把a标签的内容换成hello,preg_replace_callback(),第二个参数是个回调函数,这里采用匿名(闭包)函数来代替,否则就传入函数名即可。

回调函数的参数是匹配到结果数组,比如这里var_dump($matches),结果为:

  array(2) {
  [0]=>
  string(129) “<a href=’javascript:void(0)’ data-openwin=’true’ data-winid=” data-url=’/scm/order-detail-11932.html’>XS1000010011702130002</a>”
  [1]=>
  string(21) “XS1000010011702130002”
  }

运行代码发现结果不是预想的那样,报错$o不存在,原来忽视了最基本的作用域问题,于是回调函数里加上global $o,再运行ok。

其实在闭包函数里,还可以用use关键字来使用上文的变量。比如这里可以这么写:

<?php
function($matches) use($o){
   //...
}

两种写法都可以。

2. 封装协议

php中有很多封装协议用于url,比如http://,ftp://等等。其中有一个php://,文档中的说明:

说明:PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符, 内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。

php://output是将数据写入缓冲区,比如:

<?php
$fp = fopen("php://output","r");
fwirte($fp, "helloworld");
// 运行输出“”helloworld“”

php://input 是个可以访问请求的原始数据的只读流,比如post请求的数据:

<?php
// 假设post的数据 {"name":"laokiea"}

 var_dump(file_get_contents("php://input"));\

// result : string(12) "name=laokiea"

具体有篇文章http://www.kuitao8.com/20140727/2867.shtml

 

每日总结:类型约束,自定义错误机制,php变量比较中的一些坑

PHP是一门弱类型语言,定义的变量随着上下文的改变类型可能变来变去,这有利有弊。

从PHP5.0开始,可以强制约束变量的类型,比如对象,数组,整形等等。具体参考手册。比如:

<?php
class A{}
funtion test(A $a)
{
// 约束参数$a必须是A类的对象
//也可以改成array $a,必须是数组
//也可以改成stdClass $a,必须是对象(stdClass只是zend的一个保留类,并不是所有类的基类)
//也可以改成 callable $a,必须是回调类型
}

如果传入的参数跟约束的类型不一样,会出现一个致命的错误。如果不希望这种错误影响到代码的继续运行,可能就需要自定义错误处理函数了。

set_error_handler是php允许用户自己处理错误提示的函数。具体参考手册。

<?php

/*
自定义函数样式 :handler  ( int $errno  , string $errstr  [, string $errfile  [, int $errline  [, array $errcontext  ]]] )
第一个参数 errno,包含了错误的级别,是一个 integer。  
第二个参数 errstr,包含了错误的信息,是一个 string。
第三个参数是可选的,errfile, 包含了发生错误的文件名,是一个 string。
第四个参数是一个可选项, errline, 包含了错误发生的行号,是一个 integer。
第五个可选参数, errcontext, 是一个指向错误发生时活动符号表的 array。 也就是说,errcontext 会包含错误触发处作用域内所有变量的数组。 用户的错误处理程序不应该修改错误上下文(context)。
*/

function myHandler($no,$str,$file,$line.$context){
    if($no == E__ERROR)
    {
        //....
    }
}

set_error_handler("myHandler", null);
// 也可以用对象传入,set_error_handler(array($this, "myHandler"), null);
// 第二个参数类似error_reporting()的功能,设置哪些错误发生时,会触发自定义函数

 

今天解bug时,遇到这么一个判断表达式,$a == 0,$a是个字符串,刚开始没多想以为0等同false,只要$a不为空值,这个等式就不成立,但是var_dump一下发现竟然是true,很纳闷,后来查了一下,恍然大悟:PHP是弱类型,在比较的时候会根据语意自动转换变量的类型,比如0 == “hello”,会先把”hello”转成int类型即为0,所以这里是true。php中的严格应该用恒等于===去作比较,保证值和类型都一样。

每日总结:php位移运算

<< >>是php里的两个位移运算符。比如2<<3,11>>2,其中的计算原理是啥呢?

已3<<2为例,<<表示左移,即3所有的位往左移动2位,首先把3转换为二进制数字0000 0000 0000 0000 0000 0000 0000 0011,所有位左移2,前面的位挤掉,后面空出来的补0,那结果就是

0000 0000 0000 0000 0000 0000 0000 1100结果是12,数学原理就是3*2的2次方即:3<<2 == 3*pow(2,2);

11>>2同理,11>>2 == floor(11/pow(2,2));只不过需要向下取整,即除不足为0

<?php

// TIP
$a = "he"; 
$a{1} == $a[1] == 'h';
// {}跟[]的作用一样,表示偏移