0%

我对你仍有爱意,我对自己无能为力。
早期的 Emoji 是将一些特定的符号组合替换成图片。这种方法很难标准化,能够表达的范围也有限。 2010年,Unicode 开始为 Emoji 分配码点。也就是说,现在的 Emoji 符号就是一个文字,它会被渲染为图形。

Unicode 只是规定了 Emoji 的码点和含义,并没有规定它的样式。举例来说,码点U+1F600表示一张微笑的脸,但是这张脸长什么样,则由各个系统自己实现。
因此,当我们输入这个 Emoji 的时候,并不能保证所有用户看到的都是同一张脸。如果用户的系统没有实现这个 Emoji 符号,用户就会看到一个没有内容的方框,因为系统无法渲染这个码点。

Emoji 虽然是文字,但是无法书写,必须使用其他方法插入文档。

http://www.ruanyifeng.com/blog/2017/04/emoji.html

只是我回首来时路的每一步都走的好孤独。

1.一个汉字占多少长度与编码有关:

  • UTF-8:一个汉字=3个字节
  • GBK:一个汉字=2个字节

2.varchar(n)表示n个字符,无论汉字和英文,MySQL都能存入n个字符,仅是实际字节长度有所区别

php 把一个汉字认作几个字节:

1
2
3
<?php
$str='我';
echo strlen($str); // 3

输出 3,UTF-8编码下, 一个汉字被认作3个字节长度.

1
2
3
<?php
$str='我';
echo mb_strlen($str); // 1

整型

tinyint

占用一个字节(B), 8位(b)
有符号: -2的7次方(-128) - +2的7次方-1(127)
无符号: 0 - +2的8次方-1(255)

smallint

占用二个字节(B), 16位(b)
有符号: -2的15次方(-32768) - +2的15次方-1(32767)
无符号: 0 - +2的16次方-1(65535)

mediumint

占用三个字节(B), 24位(b)
有符号: -2的15次方(-32768) - +2的15次方-1(32767)
无符号: 0 - +2的16次方-1(65535)

int

占用四个字节(B), 32位(b)
有符号: -2的31次方 - +2的31次方-1
无符号: 0 - +2的32次方-1

bigint

占用八个字节(B), 64位(b)
有符号: -2的63次方 - +2的63次方-1
无符号: 0 - +2的64次方-1

重要说明:我们设计表所填的位数并不会影响到他所能存储的数值的大小,但是会影响到他所填充0 的效果
除了无符号(UNSIGNED)类型属性, 还有一个填充零(ZEROFILL)。 就是类型后面的位宽:
TINYINT(M)中的 M。如果这里的 M 是 3,那么默认显示三个数字,不足在其前面用零填充。
并且,这里的 M 和 TINYINT 中的最大值位数毫无关系,M 设置成 5 也可以。
如果你直接设置 ZEROFILL 属性,那么 UNSIGNED 属性也会自动被勾选,因为填充零的
首要条件就是无符号。填充零只是在显示的时候查看,而 MySQL 内部存储的还是没有填充
的数值。在 Navicat 中,设置了 ZEROFILL 属性也看不到填充的零,为了方便查阅,但在
MySQL 终端和 PHP 查询中,可以看到填充零。

字符串类型

char(2) 和 varchar(2) 都能存储 2个汉字,或者是两个英文字符.

白嘉轩后来引以豪壮的是一生里娶过七房女人。 ——陈忠实《白鹿原》

需要注意的是,HTML5已经会放弃Web SQL Database 放弃的原因如下:

This document was on the W3C Recommendation track but specification work has stopped. The specification reached an impasse: all interested implementors have used the same SQL backend (Sqlite), but we need multiple independent implementations to proceed along a standardisation path.
大概意思是:
该文件是W3C推荐标准,但规范的制定工作已经停止。该规范陷入僵局:所有感兴趣的实现者都使用了相同的SQL后端(SQLite的),但我们需要多个独立的实现沿着规范化的路径进行。

Web SQL Database 是一个运行在Html5环境下可以用Js执行CRUD的Web数据库。
在介绍它的使用前,先搞清几个误区:

  • 1、 webSQL是运行在浏览器环境的一个数据库,其数据持久化在客户端。所以不要试图连接自己的mySQL等真正意义上的数据库。不仅仅是权限,更多的是安全问题。
  • 2、对webSQL的修改,只是修改了了本地数据,如果要将数据持久化到远程数据库服务器,还是要走接口的。
  • 3、对于简单的数据存储,能用localStorage就,就不要再用webSQL了。除非是处理关系型数据库,这个时候用webSQL最合适不过了。

在使用webSQL对数据进行增删查改之前,先介绍三个核心方法:

  • 1、openDatabase:这个方法使用现有数据库或创建新数据库创建数据库对象。
  • 2、transaction:这个方法允许我们根据情况控制事务提交或回滚。
  • 3、executeSql:这个方法用于执行真实的SQL查询。

第一步:打开连接并创建数据库

1
2
3
4
5
6
7
8
9
10
11
12
var config = {  
name: 'student',
version: "1.0",
desc: '学生表',
size: 1024 * 1024
};
var dataBase = window.openDatabase(config.name, config.version, config.desc, config.size,function () {});
if (!dataBase) {
alert("数据库创建失败!");
} else {
alert("数据库创建成功!");
}

解释一下openDatabase方法打开一个已经存在的数据库,如果数据库不存在,它还可以创建数据库。几个参数意义分别是:

1,数据库名称。
2,版本号 目前为1.0(暂时不理解)
3,对数据库的描述。
4,设置数据的大小。
5,回调函数(可省略)。
初次调用时创建数据库,以后就是建立连接了。 创建的是一个sqllite数据库,完全可以用sqllite developer 打开文件,可以看到里面的数据。

第二步:创建数据表

1
2
3
4
5
6
7
8
9
10
this.createTable=function() {  
dataBase.transaction( function(tx) {
tx.executeSql(
"create table if not exists stu (id REAL UNIQUE, name TEXT)",
[],
function(tx,result){ alert('创建stu表成功'); },
function(tx, error){ alert('创建stu表失败:' + error.message);
});
});
}

executeSql函数有四个参数,其意义分别是:
1)表示查询的字符串,使用的SQL语言是SQLite 3.6.19。
2)插入到查询中问号所在处的字符串数据。
3)成功时执行的回调函数。返回两个参数:tx和执行的结果。
4)一个失败时执行的回调函数。返回两个参数:tx和失败的错误信息。

第三步:执行增删改查

添加数据:

1
2
3
4
5
6
7
8
9
this.insert = function () {  
dataBase.transaction(function (tx) {
tx.executeSql(
"insert into stu (id, name) values(?, ?)",
[id, '徐明祥'],
function () { alert('添加数据成功'); },
function (tx, error) { alert('添加数据失败: ' + error.message);
} );
});

查询数据

1
2
3
4
5
6
7
8
9
10
11
12
this.query = function () {  
dataBase.transaction(function (tx) {
tx.executeSql(
"select * from stu", [],
function (tx, result) { //执行成功的回调函数
//在这里对result 做你想要做的事情吧...........
},
function (tx, error) {
alert('查询失败: ' + error.message);
} );
});
}

上面代码中执行成功的回调函数有一参数result。 result:查询出来的数据集。其数据类型为 SQLResultSet ,就如同C#中的DataTable。
SQLResultSet 的定义为:

1
2
3
4
5
interface SQLResultSet { 
readonly attribute long insertId;
readonly attribute long rowsAffected;
readonly attribute SQLResultSetRowList rows;
};

其中最重要的属性—SQLResultSetRowList 类型的 rows 是数据集的“行” 。
rows 有两个属性:length、item 。
故,获取查询结果的某一行某一列的值 :result.rows[i].item[fieldname] 。

更新数据

1
2
3
4
5
6
7
8
9
10
11
12
this.update = function (id, name) {  
dataBase.transaction(function (tx) {
tx.executeSql(
"update stu set name = ? where id= ?",
[name, id],
function (tx, result) {
},
function (tx, error) {
alert('更新失败: ' + error.message);
});
});
}

删除数据

1
2
3
4
5
6
7
8
9
10
11
12
this.del = function (id) {
dataBase.transaction(function (tx) {
tx.executeSql(
"delete from stu where id= ?",
[id],
function (tx, result) {
},
function (tx, error) {
alert('删除失败: ' + error.message);
});
});
}

删除数据表

1
2
3
4
5
this.dropTable = function () {
dataBase.transaction(function (tx) {
tx.executeSql('drop table stu');
});
}

永远年轻,永远热泪盈眶

基本过滤选择器(重点掌握下列八个)

:first 选取第一个元素 $(“div:first”).css(“color”,”red”);
:last 选取最后一个元素 $(“div:last”).css(“color”,”red”);
:not 除去指定的选择器外的元素 $(“div:not”).css(“color”,”red”);
:even 选取索引号是偶数的元素 $(“div:even”).css(“color”,”red”);
:odd 选取索引号是奇数的元素 $(‘div:odd’).css(“color”,”red”);
:eq(index) 选取第index个元素 $(‘div:eq(5)’).css(“color”,”red”);
:gt(index) 选取索引号大于index的元素 $(‘div:gt(8)’).css(“color”,”red”);
:lt(index) 选取索引号小于index的元素$(‘div:lt(3)’).css(“color”,”red”);

属性过滤选择器(六个必须记住)

:[attribute] 选取拥有此属性的元素 $(‘div:[attribute]’).css(‘background-color’,’red’);
:[attribute=value] 选取属性值为value的元素 $(‘div:[attribute=text]’)
:[attribute!=value] 选取属性值不为value的元素 $(‘div:[attribute!=text]’)
:[attribute^=value] 选取属性值以value开始的元素 $(‘div:[attribute^=text]’)
:[attribute$=value] 选取属性值以value结尾的元素 $(‘div:[attribute$=text]’)
:[attribute*=value] 选取属性值包含value的元素 $(‘div:[attribute*=text]’)

内容过滤选择器(四个必须记住)

:contains 选取包含文本的text的元素 $(‘div:contains(.mini)’).css(“color”,”red”);
:has(id选择器名称、Class选择器名称) 选取含有选择器所匹配的元素 $(‘div:has(.mini)’).css(“color”,”red”);
:empty 选取不包含子元素的元素 $(‘div:empty’).css(“color”,”red”);
:parent 选取包含子元素的元素 $(‘div:parent’).css(“color”,”red”);

子元素过滤选择器(记住八个)

:nth-child(index) 选取每个父节点下第index个元素、偶数元素或奇数元素。
$(‘div.one :nth-child(8)’).css(‘background-color’,’#900’);
:first-child 选取每个父元素下的第一个子元素 $(‘div.one :first-child(8)’).css(‘color’,’red’);
:last-child 选取每个父元素下的最后一个子元素 $(‘div.one :last-child(8)’).css(‘color’,’red’);
:only-child 选取只有一个子元素的元素 $(‘div.one :only-child(8)’).css(‘color’,’red’);
:enabled 选取所有可用的元素 $(‘#form1 input:enabled’).val(“vaotoo.com”);
:disabled 选取所有不可用的元素 $(‘#form1 input:disabled’).val(“vaotoo.com”);
:checked 选取所有被选中的元素(一般为(HTML中)RadioButton、CheckBox标记);
$(‘input:checked’).text(“vaotoo.com”);
:selected 选取被选中的选项元素 select下拉列表标记中的option=select

1
2
3
$('select:selected').each(function(){
str += $(this).text()+",";
});

可见性过滤选择器(两个)

:hidden
:visibal

千万不要让你本来努力就能获得的东西因为懈怠而失去了机会。
“阻塞”与"非阻塞"与"同步"与“异步"不能简单的从字面理解,提供一个从分布式系统角度的回答。 ### 同步和异步

同步与异步同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。典型的异步编程模型比如Node.js举个通俗的例子:你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下”,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。

阻塞与非阻塞

阻塞与非阻塞阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。还是上面的例子,你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

https://www.zhihu.com/question/19732473/answer/20851256

其实要说梦想的话, 我的梦想就是多赚点儿钱...
扩展(Extension)一般偏底层,包(Application,Package)一般偏应用。不建议做过分的区分,在使用中能够理解和区分即可。 对于他们的管理目前有PEAR、PECL、Composer。

PEAR

PEAR是PHP扩展与应用库(the PHP Extension and Application Repository)的缩写。http://pear.php.net/
Bakken在1999年创立了PEAR项目,目标是试图定义一种标准,帮助开发者编写可移植、可重用的代码,避免重复发明“车轮”。代码均是PHP编写的。
涵盖了页面呈现、数据库访问、文件操作、数据结构、缓存操作、网络协议、WebService 等许多方面,用户可以通过下载这些类库,并适当的作一些定制以实现自己需要的功能。
但是随着Git、github等发展,其逐渐被Composer包管理替代。

PECL

“PHP Extension Community Library”的缩写,即PHP 扩展库。https://pecl.php.net/
PECL是使用C语言开发的,通常用于补充一些用PHP难以完成的底层功能,往往需要重新编译或者在配置文件中设置后才能在用户自己的代码中使用。
2种使用方式:
命令模式,和pear的命令一致,通过man pecl显示此命令的用法
编译模式,即phpize

Composer

PHP包管理工具,在“帮助开发者编写可移植、可重用的代码,避免重复发明“车轮””上和PEAR有相同的目标和思想,但是composer的包一般放在github、bitbucket等上,通过packagist 可以浏览上传等

以Yaml安装使用为例

pecl安装

http://php.net/manual/zh/install.pecl.pear.php

pecl install yaml
这将下载 yaml 的源代码,编译之,并将 yaml.so 安装到扩展库目录 extension_dir 中。然后 yaml.so就可以通过 php.ini 加载了。

编译安装

http://php.net/manual/zh/install.pecl.phpize.php
有时候不能用 pecl 安装命令。这可能是因为在防火墙后面,或者是因为想要安装的扩展库还没有 PECL 兼容的包,例如 SVN 中尚未发布的扩展库。
如果要编译这种扩展库,可以用更底层的编译工具来手工进行编译。phpize 命令是用来准备 PHP 扩展库的编译环境的。

1
2
3
4
5
6
7
$ wget https://pecl.php.net/get/yaml-1.3.0.tgz
$ tar -xzvf yaml-1.3.0.tgz
$ cd yaml-1.3.0
$ phpize
$ ./configure
$ make
$ make install

成功的安装将创建 yaml.so 并放置于 PHP 的扩展库目录 extension_dir 中。需要调整 php.ini 加入 extension=yaml.so 这一行之后才能使用此扩展库。
使用 phpize –help 命令可以显示此命令用法。

composer

可以参看文档 http://www.jianshu.com/p/f0b9a319711f

composer require mustangostang/spyc
目前主流的方式,对于偏底层的扩展通过编译方式安装,对于偏应用的通过composer方式。

http://www.jianshu.com/p/d8b75dbc852a

几年前踏上火车那一刻都还没有意识到,从此故乡只有冬夏,再无春秋。
如果不需要傳送參數或是使用GET method傳送

可以直接使用fopen()或是file_get_contents()函式獲得回應內容
但是如果需要不經過表單就送出POST給某URL
就需要使用curl相關函式或是fsockopen()傳送

curl的用法比較簡單
可以咕狗看看(但是php必須要先安裝curl才可以用)
這邊要講的是fsockopen()

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
44
45
//接收POST參數的URL
$url = 'http://www.google.com';
//POST參數,在這個陣列裡,索引是name,值是value,沒有限定組數
$postdata = array('post_name'=>'post_value','acc'=>'hsin','nick'=>'joe');
//函式回覆的值就是取得的內容
$result = sendpost($url,$postdata);

function sendpost($url, $data){
//先解析url 取得的資訊可以看看http://www.php.net/parse_url
$url = parse_url($url);
$url_port = $url['port']==''?(($url['scheme']=='https')?443:80):$url['port'];
if(!$url) return "couldn't parse url";
//對要傳送的POST參數作處理
$encoded = "";
while(list($k,$v)=each($data)){
$encoded .= ($encoded?'&':'');
$encoded .= rawurlencode($k)."=".rawurlencode($v);
}
//開啟一個socket
$fp = fsockopen($url['host'],$url_port);
if(!$fp) return "Failed to open socket to ".$url['host'];
//header的資訊
fputs($fp,'POST '.$url['path'].($url['query']?'?'.$url['query']:'')." HTTP/1.0rn");
fputs($fp,"Host: ".$url['host']."n");
fputs($fp,"Content-type: application/x-www-form-urlencodedn");
fputs($fp,"Content-length: ".strlen($encoded)."n");
fputs($fp,"Connection: closenn");
fputs($fp,$encoded."n");
//取得回應的內容
$line = fgets($fp,1024);
if(!eregi("^HTTP/1.. 200", $line)) return;
$results = "";
$inheader = 1;
while(!feof($fp)){
$line = fgets($fp,2048);
if($inheader&&($line == "n" || $line == "rn")){
$inheader = 0;
}elseif(!$inheader){
$results .= $line;
}
}
fclose($fp);
return $results;
}

http://inspiregate.com/programming/php/26-php-to-use-fsockopen-to-send-post-to-another-url-and-get-to-respond-to-the-content.html

你考95分是因为你的实力只有95分。我考一百分是因为这个卷子只有100分。

$_POST

$_POST 是获取表单 POST 过来数据(body部分)的最常用方法,上传的文件信息使用 $_FILES 获取。

php://input

可以读取没有处理过的POST数据。
相较于$HTTP_RAW_POST_DATA而言,它给内存带来的压力较小,并且不需要特殊的php.ini设置。
php://input不能用于enctype=multipart/form-data

由于 php://input 只是数据流,我们可以使用 file_get_contents() 函数去获取它的内容:

1
2
$post_data = file_get_contents('php://input');
print_r($post_data);

获取到的内容和 $HTTP_RAW_POST_DATA 是一样的。

原始数据

当浏览器从表单发送 POST 请求的时候,默认的 media type 是 “application/x-www-form-urlencoded”,意思就是字段名和值都编码了,每个 key-value 对使用 ‘&’ 字符分隔开,key 和 value 使用 ‘=’ 分开,并且 key 和 value 中的空格都会被替换成 + ,其他特殊字符都会被使用 urlencode 方式进行编码。
比如下面的 key-value 对:

1
2
3
name: Jonathan Doe
age: 23
formula: a + b == 13%!

会被编码下面的原始数据:
name=Jonathan+Doe&age=23&formula=a+%2B+b+%3D%3D+13%25%21
PHP 会解析这些原始的 POST 数据,并且格式化成数组,填充到 $_POST 中:

1
2
3
4
5
6
Array
(
[name] => Jonathan Doe
[age] => 23
[formula] => a + b == 13%!
)

其他

很多时候,接收到不是网页 POST 过来的数据,而是可能通过其他方式 POST 过来的 “text/xml” 格式的数据,这些内容无法解析成 $_POST 数组,这个时候我们就需要原始的 POST 数据进行处理。

重要

PHP 7 已经取消了 $HTTP_RAW_POST_DATA,请用 php://input 代替。

别人说你变了,是因为你没有按照他的想法活罢了。。。
先来一段 PHP 连接 MySQL 的经典代码:
1
2
3
4
5
6
7
8
<?php
$con = mysql_connect("localhost", "root", "123456");
if (!$con) {
die("Could not connect: " . mysql_error());
}
// some code
mysql_close($con);
?>
### mysql_pconnect 首先,当连接的时候本函数将先尝试寻找一个在同一个主机上用同样的用户名和密码已经打开的(持久)连接,如果找到,则返回此连接标识而不打开新连接。

其次,当脚本执行完毕后到 SQL 服务器的连接不会被关闭,此连接将保持打开以备以后使用(mysql_close() 不会关闭由 mysql_pconnect() 建立的连接)。

1
2
3
4
5
6
7
8
9
<?php
// pconnect.php
$con = mysql_pconnect("localhost", "root", "123456");
// connect.php
// $con = mysql_connect("localhost","root","123456");
if (!$con) {
die('Could not connect: ' . mysql_error());
}
sleep(10)

注意,此种连接仅能用于模块版本的 PHP。(我用的是 Nginx+PHP-FPM 模式,而非 Apache+php_module 模式。 )

最重要的东西,用眼睛是看不见的。
在PHP5中有两个魔术方法__sleep()方法和__wakeup()方法,在对象串行化的时候,会调用一个__sleep()方法来完成一 些睡前的事情;而在重新醒来,即由二进制串重新组成一个对象的时候,则会自动调用PHP的另一个函数__wakeup(),做一些对象醒来就要做的动作。

__sleep()函数不接受任何参数, 但返回一个数组,其中包含需要串行化的属性。末被包含的属性将在串行化时被忽略,如果没有__sleep()方法,PHP将保存所有属性。

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
44
45
46
<?php

class Person
{
//下面是人的成员属性
var $name; //人的名子
var $sex; //人的性别
var $age; //人的年龄

//定义一个构造方法参数为属性姓名$name、性别$sex和年龄$age进行赋值
function __construct($name = "", $sex = "", $age = "")
{

$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}

//这个人可以说话的方法, 说出自己的属性
public function say()
{
echo "我的名子叫:" . $this->name . " 性别:" . $this->sex . " 我的年龄是:" . $this->age . "<br>";
}

public function __sleep()
{
// TODO: Implement __sleep() method.
$arr = array("name", "age"); // 此时,属性$sex将被删除!!!
return $arr;
}


public function __wakeup()
{
// TODO: Implement __wakeup() method.
$this->age = 40; //重新生成对象时,并重新赋值$age为40
}

}

$p1 = new Person("张三", "男", 20);
$p1_string = serialize($p1); //把一个对象串行化,返一个字符串
echo $p1_string . "<br>"; //串行化的字符串我们通常不去解析
$p2 = unserialize($p1_string); //把一个串行化的字符串反串行化形成对象$p2
$p2->say();