0%

自此无心爱良夜,任他明月下西楼。

按逗号分隔列表

1
2
3
L = [1,2,3,4,5]
s1 = ','.join(str(n) for n in L)
print s1

有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?

1
2
3
4
5
for i in range(1,5):
for j in range(1,5):
for k in range(1,5):
if( i != k ) and (i != j) and (j != k):
print i,j,k

交换两个变量

1
2
3
4
5
6
7
8
9
10
11
12
def exchange(a,b):
a,b = b,a
return (a,b)

if __name__ == '__main__':
x = 10
y = 20
print('x = %d,y = %d' % (x,y))

x,y = exchange(x,y)

print('x = %d,y = %d' % (x,y))

當我們懂得珍惜平凡的幸福時,就已經成了人生的贏家。
撤销commit有很多方法,个人比较推荐用 reset 或 rebase -i,底下将会同时介绍 revert 和 reset 的方法。

commit 如下

A -> B -> C -> D -> E
想要还原到 commit C 之后的状态 (也就是把 D 和 E 回退)

revert

用:

1
2
git revert E
git revert D

结果:

A -> B -> C -> D -> E -> F -> G
F 是还原 commit E 修改结果的 commit

G 是还原 commit D 修改结果的 commit

因此,revert 只会让 commit 继续往前

优点是可以针对某个 commit 进行还原,并且留下还原记录

rebase -i

假如想要抽掉某个 commit 又不想留下记录, rebase -i 就很好用了

假如只想要还原 D 变成:

A -> B -> C -> E

则用命令

1
git rebase -i C

这时候会出现文字编辑

1
2
pick D xxx
pick E ooo

把 pick D xxx 整列移除后储存就可以了,若中间有遇到冲突,则必须自行修正后再继续

1
2
git add .
git rebase --continue

reset

用于做整段 commits 的还原

例如希望还原到 B commit 以后的状态变成

A -> B

1
git reset B

那么 git 会将 log 中的 C, D, E 都清除

但档案内容没有任何变动,因此会看到 C, D, E 修改的档案处在 unstaged 阶段

若针对部分档案还原可以用

1
git checkout [file path]

若要全部还原可用

1
git checkout -f

结论

还没 push 前,个人倾向不产生太多 commit。因此我都会用 rebase -i 进行编修, 顺便合并或reword 一些 commit

某个特殊情况, 例如发现某个 commit 里面包含了不相干的档案, 欲重新 commit 时,就会先用 rebase -i 把欲修改的 commit 换到后面(较新), 然后再用 reset 重新 stage + commit。

低头要有勇气,抬头要有底气
Opcode是一种PHP脚本编译后的中间语言,就像Java的ByteCode,或者.NET的MSL,举个例子,比如你写下了如下的PHP代码:
1
2
3
4
5
 <?php
echo "Hello World";
$a = 1 + 1;
echo $a;
?>
PHP执行这段代码会经过如下4个步骤(确切的来说,应该是PHP的语言引擎Zend)
  • 1.Scanning(Lexing) ,将PHP代码转换为语言片段(Tokens)
  • 2.Parsing, 将Tokens转换成简单而有意义的表达式
  • 3.Compilation, 将表达式编译成Opocdes
  • 4.Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。

题外话:现在有的Cache比如APC,可以使得PHP缓存住Opcodes,这样,每次有请求来临的时候,就不需要重复执行前面3步,从而能大幅的提高PHP的执行速度。

那什么是Lexing? 学过编译原理的同学都应该对编译原理中的词法分析步骤有所了解,Lex就是一个词法分析的依据表。 Zend/zend_language_scanner.c会根据Zend/zend_language_scanner.l(Lex文件),来输入的 PHP代码进行词法分析,从而得到一个一个的“词”,PHP4.2开始提供了一个函数叫token_get_all,这个函数就可以讲一段PHP代码 Scanning成Tokens;

http://www.laruence.com/2008/06/18/221.html

我讨厌变老,当我们力量衰弱的时候如果出现更强的敌人,我会觉得很恐怖,很遗憾。
### 基本说明 先定义一个User类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

class User{
public $username;
private $password;

public function login()
{
echo "login";
}

public function register()
{
echo "registert";
}
}
实例化并调用其中的方法
1
2
$user = new User();
$user->login();
实例化User类的反射类并调用其中的一个方法
1
2
$reUser = new \ReflectionClass('User');
var_dump($reUser->getFileName());
说明:ReflectionClass类获取类相关信息,如获取属性、方法、文档注释等;

实际应用

  • 通过_call可以调用类一些不确定的方法,这个时候就可以通过反射类ReflectionClass来判断类方法是否存在,是不是可见可调用的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
class A{
public function actionA(){
echo "I'am A\n";
}
}
class B{
private function actionB (){
echo "I'am B\n";
}
}
class test{
private $objects;
public function __construct(){
$this->objects=array();
}
public function add($t){
$this->objects[]=$t;
}
private function pp(){
}
//调用不可见的方法或者没有的方法都会走到这里
public function __call($name,$args){
foreach($this->objects as $object){
$class=new ReflectionClass($object);
if(!$class->hasMethod($name)){
continue;
}
$method=$class->getMethod($name);
if($method&&$method->isPublic()&&!$method->isAbstract()){
$method->invoke($object,$args);
return ;
}
}
echo "not method!\n";
}
}
$t=new test();
$t->add(new A());
$t->add(new B());
$t->pp(); //不可见方法
$t->actionA();
$t->actionB();

无她,唯手熟尔
### 例子

调用函数

1
2
3
4
5
6
7
8
9
<?php
function a($b, $c)
{
echo $b;
echo $c;
}
call_user_func_array('a', array("111", "222"));
//显示 111 222
?>

调用静态方法

1
2
3
4
5
6
7
8
9
10
11
<?php
Class ClassA
{
public static function bc($b, $c) {
$bc = $b + $c;
echo $bc;
}
}
call_user_func_array(array('ClassA','bc'), array("111", "222"));
//显示 333
?>

说明:这样就可以用数组的方式给类的方法传入必要的参数

调用普通方法

1
2
3
4
5
6
7
8
9
10
<?php

class MyClass {
public function hello() {
echo "Hello, World!";
}
}

$a = new MyClass();
call_user_func(array($a, 'hello'));

我的肺功能快衰竭了,没有两天了,请大家不要打钱了,谢谢大家这段时间的帮助,希望来生没有痛苦。---魏则西

最近在开发一个PHP程序时遇到了下面的错误:

1
PHP Fatal error: Allowed memory size of 268 435 456 bytes exhausted

错误信息显示允许的最大内存已经耗尽。遇到这样的错误起初让我很诧异,但转眼一想,也不奇怪,因为我正在开发的这个程序是要用一个foreach循环语句在一个有4万条记录的表里全表搜索具有特定特征的数据,也就是说,一次要把4万条数据取出,然后逐条检查每天数据。可想而知,4万条数据全部加载到内存中,内存不爆才怪。

毕竟编程这么多年,我隐约记得PHP里提供有非一次全部加载数据的API,是像处理流媒体那样,随用随取随丢、数据并不会积累在内存的查询方法。经过简单的搜索,果然在官方网站上找到的正确的用法。

这个问题在PHP的官方网站上叫缓冲查询和非缓冲查询(Buffered and Unbuffered queries)。 PHP的查询缺省模式是缓冲模式。也就是说,查询数据结果会一次全部提取到内存里供PHP程序处理。这样给了PHP程序额外的功能,比如说,计算行数,将 指针指向某一行等。更重要的是程序可以对数据集反复进行二次查询和过滤等操作。但这种缓冲查询模式的缺陷就是消耗内存,也就是用空间换速度。

相对的,另外一种PHP查询模式是非缓冲查询,数据库服务器会一条一条的返回数据,而不是一次全部返回,这样的结果就是PHP程序消耗较少的内存,但却增加了数据库服务器的压力,因为数据库会一直等待PHP来取数据,一直到数据全部取完。

很显然,缓冲查询模式适用于小数据量查询,而非缓冲查询适应于大数据量查询。

对于PHP的缓冲模式查询大家都知道,下面列举的例子是如何执行非缓冲查询API。

非缓冲查询方法一: mysqli

1
2
3
4
5
6
7
8
9
10
11
<?php 
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
$uresult = $mysqli->query("SELECT Name FROM City", MYSQLI_USE_RESULT);

if ($uresult) {
while ($row = $uresult->fetch_assoc()) {
echo $row['Name'] . PHP_EOL;
}
}
$uresult->close();
?>

非缓冲查询方法二: pdo_mysql

1
2
3
4
5
6
7
8
9
10
11
<?php 
$pdo = new PDO("mysql:host=localhost;dbname=world", 'my_user', 'my_pass');
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

$uresult = $pdo->query("SELECT Name FROM City");
if ($uresult) {
while ($row = $uresult->fetch(PDO::FETCH_ASSOC)) {
echo $row['Name'] . PHP_EOL;
}
}
?>

非缓冲查询方法三: mysql

1
2
3
4
5
6
7
8
9
10
11
<?php 
$conn = mysql_connect("localhost", "my_user", "my_pass");
$db = mysql_select_db("world");

$uresult = mysql_unbuffered_query("SELECT Name FROM City");
if ($uresult) {
while ($row = mysql_fetch_assoc($uresult)) {
echo $row['Name'] . PHP_EOL;
}
}
?>

http://www.codeceo.com/article/php-foreach-memory.html

把一切平凡的事做好即不平凡,把一切简单的事做好即不简单。
PHP 5没有提供一种简单的机制来生成密码学上强壮的随机数,但是PHP 7通过引入几个CSPRNG函数来解决了这个问题。 ### 什么是CSPRNG

引用维基百科,一个密码学上安全的伪随机数发生器(Cryptographically Secure Pseudorandom Number Generator 缩写CSPRNG)是一个伪随机数生成器(PRNG),其生成的伪随机数适用于密码学算法。

CSPRNG可能主要用于:

密钥生成(例如,生成复杂的密钥)
为新用户产生随机的密码
加密系统
获得高级别安全性的一个关键方面就是高品质的随机性

PHP7 中的CSPRNG

PHP 7引入了两个新函数可以用来实现CSPRNG: random_bytes 和 random_int。

random_bytes 函数返回一个字符串,接受一个int型入参代表返回结果的字节数。

例子:

1
2
3
4
$bytes = random_bytes('10');
var_dump(bin2hex($bytes));
//possible ouput: string(20) "7dfab0af960d359388e6"
random_int 函数返回一个指定范围内的int型数字。

例子:

1
2
var_dump(random_int(1, 100));
//possible output: 27

后台运行环境

以上函数的随机性不同的取决于环境:

在window上,CryptGenRandom()总是被使用。
在其他平台,arc4random_buf()如果可用会被使用(在BSD系列或者具有libbsd的系统上成立)
以上都不成立的话,一个linux系统调用getrandom(2)会被使用。
如果还不行,/dev/urandom 会被作为最后一个可使用的工具
如果以上都不行,系统会抛出错误
一个简单的测试

PHP5 呢

缺省情况下,PHP5 不提供强壮的随机数发生器。实际上,还是有选择的比如 openssl_random_pseudo_bytes(), mcrypt_create_iv() 或者直接使用fread()函数来使用 /dev/random 或 /dev/urandom 设备。也有一些包比如 RandomLib 或 libsodium.

如果你想要开始使用一个更好的随机数发生器并且同时准备好使用PHP7,你可以使用Paragon Initiative Enterprises random_compat 库。 random_compat 库允许你在 PHP 5.x project.使用 random_bytes() and random_int()

这个库可以通过Composer安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
composer require paragonie/random_compat
require 'vendor/autoload.php';
$string = random_bytes(32);
var_dump(bin2hex($string));
// string(64) "8757a27ce421b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2aaec6f"
$int = random_int(0,255);
var_dump($int);
// int(81)
//random_compat 库和PHP7使用不同的顺序:

fread() /dev/urandom if available
mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)
COM('CAPICOM.Utilities.1')->GetRandom()
openssl_random_pseudo_bytes()

想知道为什么是这个顺序建议阅读 documentation.

这个库的一个简单应用用来产生密码:

1
2
3
4
5
6
7
8
9
$passwordChar = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$passwordLength = 8;
$max = strlen($passwordChar) - 1;
$password = '';
for ($i = 0; $i < $passwordLength; ++$i) {
$password .= $passwordChar[random_int(0, $max)];
}
echo $password;
//possible output: 7rgG8GHu

总结

你总是应该使用一个密码学上安全的伪随机数生成器,random_compat 库提供了一种好的实现。

如果你想要使用可靠的随机数据源,如你在本文所见,建议尽快使用 random_int 和 random_bytes.

http://www.codeceo.com/article/php-random.html

我们走得太快,灵魂都跟不上了 ...
今天偶然看到鸟哥(php)的博客‘ 深入理解PHP原理之foreach’,可惜自己功力尚欠,内部的机理看不明白,但也稍微记录一下,毕竟这个问题自己以前从来没有考虑过。 #### 以下内容摘自鸟哥的博客 经常会有人问我, PHP的数组, 如果用foreach来访问, 遍历的顺序是固定的么? 以什么顺序遍历呢? 比如:
1
2
3
4
5
6
7
<?php
$arr['laruence'] = 'huixinchen';
$arr['yahoo'] = 2007;
$arr['baidu'] = 2008;
foreach ($arr as $key => $val) {
//结果是什么?
}
结果: huixinchen20072008

又比如:

1
2
3
4
5
6
7
<?php
$arr[2] = 'huixinchen';
$arr[1] = 2007;
$arr[0] = 2008;
foreach ($arr as $key => $val) {
//现在结果又是什么?
}

结果:
huixinchen 2007 2008

要完全了解清楚这个问题, 我想首先应该要大家了解PHP数组的内部实现结构………
在PHP中, 数组是用一种HASH结构(HashTable)来实现的, PHP使用了一些机制, 使得可以在O(1)的时间复杂度下实现数组的增删, 并同时支持线性遍历和随机访问.

说明

PHP中遍历数组的顺序, 是和元素的添加先后相关的;
所以, 如果你想在数字索引的数组中按照索引大小遍历, 那么你就应该使用for, 而不是foreach

1
2
3
for($i=0,$l=count($arr); $i<$l; $i++) {
//这个时候,不能认为是顺序遍历(线性遍历)
}

幸福,不是长生不老,不是大鱼大肉,不是权倾朝野。幸福是每一个微小的生活愿望达成。当你想吃的时候有得吃,想被爱的时候有人来爱你。

Laravel 默认使用 file 的方式来实现 session的。她并不用php原生的$_SESSION,php原生的session要看php.ini的位置,所以忽略php相关的session函数,例如session_start(), $_SESSION。Laravel在运行过程中会在app/storage/session/目录写入session的信息,所以这个目录需要有写权限,否者session就无法写入成功。

Laravel除了使用默认的file作为session的实现,还支持cookie, Memcached, Redis 和数据库的后端驱动作为session的实现。必要的时候还需要自己实现一个session的实现方式,比如在微信公众账号和用户的交互,这中session就无法直接使用,因为每次都是微信服务器来请求,无法通过请求的来源来辨别用户。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//session的永久保存(在不过期范围内)
Session::put('key', 'value');

//等同于PHP的原生session
$_SESSION['key'] = 'value';

//get操作
$value = Session::get('key', 'default');

//去除操作并删除,类似pop概念
$value = Session::pull('key', 'default');

//检测是否存在key
Session::has('users');

//删除key
Session::forget('key');

laravel的session中flash概念

这个flash两次请求有效(本次和下次请求有效),与本次请求取操作多少次无关。

1
2
3
4
5
6
7
8
//保存key,value
Session::flash('key', 'value');

//取值方法还是一样的
Session::get('key');

//刷新快闪数据时间,保持到下次请求
Session::keep(array('username', 'email'))

这个flash的概念和上面的put的概念不太一样。

put :这个对应只要session不过期,基本上是永久保存,下次请求也是存在的。
flash :保存的值,本次请求可以使用,下次http请求可以使用,再下一次就不存在了。
也就是说下一次的请求用完就被销毁了,不会让session的值变的越来越大,可以保存一些临时的数据。

这中情况的使用场景比如有:

用户请求了页面,出现错误信息,重定向到一个新的页面,需要展示之前的数据。(虽然可以通过url参数来传递,处理不好可能会有xss漏洞)。
用户访问了一个页面,过滤器发现没权限,保存当前页面url,重定向到登录页面,登录成功,取出值,重定向到原先的页面。(这里可能需要刷新保存的快闪数据)

session落地的时间

我天真的以为使用了Session::put函数就能保存这个变量了。于是我的代码这样写:

1
2
3
4
5
6
7
8
9
class LoginController {

public function login(){
Session::put('key','value');
print_r( Session::all() ); //取出来看看是否put成功
exit; //习惯性的调试都exit,不执行后续代码
//return Redirect::to(/); 框架在return后还会有后续的代码执行的
}
}

结果下次请求就是找不到本次的Session,而且看app/storage/session目录就是没有文件生成。总感觉不对劲啊。

后来看到网络上有个方法Session::save(),于是我也用了下,居然发现成功的生成了session的文件。于是我感觉到,Laravel不用php原生的session,那么在controller之后应该做了一些事情,将session写入到文件中,而不是每次put操作都写操作,这样会IO操作太频繁的,影响性能的。
可以看见,在调用完controller之后,调用了session->save()的方法,来主动的保存session。这样session才能落地保存起来,如果在controller或者view里面写了exit;,那么session是不会被保存的,除非主动的写Session::save()才能手工的保存起来。

https://www.chenyudong.com/archives/laravel-session-use.html

我亦只有一个一生,不能慷慨赠予我不爱的人。

设置php.ini配置文件

第一种方法即设置php.ini配置文件,设置session.gc_maxlifetime和session.cookie_lifetime节点属性值,当然也可以使用ini_set函数改变当前上下文环境的属性值:

1
2
ini_set('session.gc_maxlifetime', "3600"); // 秒
ini_set("session.cookie_lifetime","3600"); // 秒

设置Session时间戳

第二种方法即设置Session时间戳,比如下面的办法。
在登录成功时设置时间戳为当前时间推后1小时,$_SESSION['expiretime'] = time() + 3600;。在检查用户登录情况使用如下代码:

1
2
3
4
5
6
7
8
9
if(isset($_SESSION['expiretime'])) {
if($_SESSION['expiretime'] < time()) {
unset($_SESSION['expiretime']);
header('Location: logout.php?TIMEOUT'); // 登出
exit(0);
} else {
$_SESSION['expiretime'] = time() + 3600; // 刷新时间戳
}
}

http://wangye.org/blog/archives/933/
http://www.laruence.com/2012/01/10/2469.html