jQuery源码分析
前言
有时候我在想jQuery为什么可以直接$操作,可以拥有比原生js更便利的DOM操作,而且只要你想就可以直接链式操作下去
核心框架
揭开一万多行代码的jQuery核心代码:
(function(window, undefined) { function jQuery(selector){ return new jQuery.fn.init(selector) } jQuery.fn = jQuery.prototype = { init: function () { } } jQuery.fn.init.prototype = jQuery.fn; window.jQuery = window.$ = jQuery; })(window)
- 闭包结构传参window
- 闭包结构传入实参window,然后里面用形参接收
- 减少内部每次引用window的查询时间
- 方便压缩代码
- 闭包结构传入实参window,然后里面用形参接收
- 形参undefined
- 因为ie低版本的浏览器可以给undefined赋值成功,所以为了保证undefined的纯洁给它一个形参的位置而没有实参,保证了它一定是undefined
- jQuery传参selector
- selector可以是一对标签,可以是id、类、后代、子代等等,可以是jQuery对象,
- jQuery原型对象赋值
- 方便扩展jQuery的原型方法
- return 实例化原型方法init
- 其实就是为了我们每次使用$不用new $();
- 为什么jQuery要new自己的原型方法呢,因为不new自己的就要new其他的函数返回,那干嘛不自己利用自己
- jQuery原型对象赋值给jQuery原型方法init的原型
- 因为内部给jQuery原型每扩展一个方法init也会有该方法,是不是很酷炫,init有了那么$()出来的jQuery对象是不是也有啦
- 给window暴露可利用成员jQuery,$
- 给window暴露后那么全局都可以直接使用了jQuery和$了
- 至于为什么有$,因为短啊,当然你也可以每次jQuery()来使用
御用选择器-Sizzle
- Sizzle也是jQuery的根本,当然了你也单独使用Sizzle
- 上面说过$(selector)的参数selector可以是id、类、后代、子代等等,可以是jQuery对象,那么咱们每次$一下就可以心想事成的得到我们想要的jQuery对象是怎么办到的呢,没错,就是因为Sizzle,Sizzle封装了获取各种dom对象的方法,并且会把他们包装成jQuery对象
- 浏览器能力测试
- Sizzle内部有个support对象,support对象存储着正则测试浏览器能力的结果
- 对于有能力问题的选择器使用通用兼容方案解决(繁琐的判断代码)
- 正则
- 正则表达式在jQuery中使用的还是比较多的,正则的使用可以很大的提交我们对数据的处理效率
- 判断
- 判断是在init内部判断selector的类型,
- 列如可能是个html标签,那么直接create一个selector标签的DOM对象包装成jQuery对象return出去
- 列如可能是个id名、类名、标签名等等,那么直接通过Sizzle获取到DOM对象包装成jQuery对象return出去
- 判断是在init内部判断selector的类型,
- 包装
- 我已经说了很多次的包装了,没错,jQuery对象其实也是个伪数组,这也是它的设计巧妙之处,因为用数组存储数据方便我们去进行更多的数据处理,比如
$("div").css("color": "red")
,那么jQuery会自动帮我们隐式迭代、再给页面上所有div包含的文字颜色设置为red,简单粗暴的一行代码搞定简直是程序猿的最爱
- 我已经说了很多次的包装了,没错,jQuery对象其实也是个伪数组,这也是它的设计巧妙之处,因为用数组存储数据方便我们去进行更多的数据处理,比如
对外扩展-extend
- jQuery核心的结构处理完毕之后基本上就可以对外使用了,但是我们知道我们是可以基于jQuery来实现插件的,包括jQuery自己可扩展性也必须要求他要对外提供一个接口方便进行二次开发,所以有了extend方法
- 简单的extend就是混入,列子:
function extend(obj) { var k; for(k in obj) { this[k] = obj[k]; } } Baiya.extend = extend; Baiya.fn.extend = extend;
对静态方法的和实例方法的扩展都要有,比如each方法,可以$.each来使用,也可以是$("div").each来使用
- 之后jQuery一些方法都是基于extend来扩展的,当然我们自己也可以基于jQuery扩展方法
DOM操作
- DOM操作也是jQuery的一大特点,因为它太好用了,包含了我们所能想到的所有使用场景,完善了增删查改常用的方法
- jQuery获取和设置类的方法如html()/css()/val()等等这些传参是设置值不传参是获取值
##链式编程
- jQuery是支持链式编程的,只要你想你就可以一行代码写完所有的功能,这是怎么做到的呢
- 每一个改变原型链的方法都会把当前的this对象保存成他自己的属性,然后可以调用end方法找到上一级链从而方便我们可以进行链式操作
事件操作
- jQuery的事件操作一般可以通过click类(mouseover/mouseleave等等)和on来使用,但是click类的实现是调用on的
- on的实现是对原生的onclick类的处理,因为相同的原生的事件在同一个DOM对象上只能被绑定一次,如果再次绑定会覆盖掉上一次的,所以jQuery帮我们封装了事件的存储,把相同的事件分成一个数组存储在一个对象里面,然后对数组进行遍历,依次调用数组里存储的每个方法
- on实现之后会把所有的事件处理字符串处理一下用on来改造一下,如下:
Baiya.each(("onclick,onmousedown,onmouseenter,onmouseleave," + "onmousemove,onmouseout,onmouseover,onmouseup,onfocus," + "onmousewheel,onkeydown,onkeypress,onkeyup,onblur").split(","), function (i, v) { var event = v.slice(2); Baiya.fn[event] = function (callback) { return this.on(event, callback); } });
属性操作
- jQuery也提供给了我们方便的属性操作,底层就是对原生方法的包装,处理兼容性问题,如果jQuery不对IE浏览器的兼容处理的话,那么它的代码量可能会缩一半,当然锅不能全部甩给IE,比如innerText方法火狐是不支持的,但是支持textContent方法,所以jQuery会尽可能的处理这种浏览器带来的差异
样式操作
- 基本思想如上
Ajax操作
- Ajax可以说是前端的跨越性进步,毫不夸张的说如果没有Ajax的发展,那么今天的前端可能不叫前端,可能是美工……
- Ajax是什么?
- 在我的理解来看Ajax就是一个方法,这个方法遵循着http协议的规范,我们可以使用这个方法来向服务器请求少量的数据,有了数据之后我们就可以操作DOM来达到局部更新网页的目的,这是一个非常酷的事情
- jQuery的Ajax是基于XMLHttpRequest的封装,当然了他也有兼容性问题,具体的封装见我之前的文章简单的ajax封装
- 具体就是区别get和post请求的区别,get请求的传参是直接拼接在url结尾,而post请求需要在send()里面传递,并且post请求还要设置请求头setRequestHeader("content-type", "application/x-www-form-urlencoded")
- 请求后对json或者text或者xml的数据进行处理就可以渲染到页面了
提到Ajax就不得不提到跨域了
- 跨域简单的来说限制了非同源(ip/域名/端口/协议)的数据交互,当然这肯定是极好的,因为如果不限制那么你的网页别人也可以操作是不是很恐怖
- 但是有些情况下我们需要调用别人的服务器数据,而且别人也愿意怎么办呢,程序员是很聪明的,html标签中img,script,link等一些带有src属性的标签是可以请求外部资源的,img和link得到的数据是不可用的,只有script标签请求的数据我们可以通过函数来接收,函数的参数传递可以是任何类型,所以创建一个函数,来接收,参数就是请求到的数据,而对方的数据也要用该函数来调用就可以实现跨域了
- 简单封装jsonp实现
// url是请求的接口 // params是传递的参数 // fn是回调函数 function jsonp(url, params, fn){ // cbName实现给url加上哈希,防止同一个地址请求出现缓存 var cbName = `jsonp_${(Math.random() * Math.random()).toString().substr(2)}`; window[cbName] = function (data) { fn(data); // 获取数据后移除script标签 window.document.body.removeChild(scriptElement); }; // 组合最终请求的url地址 var querystring = ''; for (var key in params) { querystring += `${key}=${params[key]}&`; } // 告诉服务端我的回调叫什么 querystring += `callback=${cbName}`; url = `${url}?${querystring}`; // 创建一个script标签,并将src设置为url地址 var scriptElement = window.document.createElement('script'); scriptElement.src = url; // appendChild(执行) window.document.body.appendChild(scriptElement); }
Animate
- 很抱歉的是jQuery的动画源码我并没有阅读,但是我自己封装了一个动画函数,之后的源码阅读会补上的
- 封装的代码
// element设置动画的DOM对象 // attrs设置动画的属性 object // fn是回调函数 function animate(element, attrs, fn) { //清除定时器 if(element.timerId) { clearInterval(element.timerId); } element.timerId = setInterval(function () { //设置开关 var stop = true; //遍历attrs对象,获取所有属性 for(var k in attrs) { //获取样式属性 对应的目标值 var target = parseInt(attrs[k]); var current = 0; var step = 0; //判断是否是要修改透明度的属性 if(k === "opacity") { current = parseFloat( getStyle(element, k)) * 100 || 0; target = target * 100; step = (target - current) / 10; step = step > 0 ? Math.ceil(step) : Math.floor(step); current += step; element.style[k] = current / 100; //兼容ie element.style["filter"] = "alpha(opacity="+ current +")"; }else if(k === "zIndex") { element.style[k] = target; } else { //获取任意样式属性的值,如果转换数字失败,返回为0 current = parseInt(getStyle(element, k)) || 0; step = (target - current) / 10; console.log("current:" + current + " step:" + step); step = step > 0 ? Math.ceil(step) : Math.floor(step); current += step; //设置任意样式属性的值 element.style[k] = current + "px"; } if(step !== 0) { //如果有一个属性的值没有到达target ,设置为false stop = false; } } //如果所有属性值都到达target 停止定时器 if(stop) { clearInterval(element.timerId); //动画执行完毕 回调函数 if(fn) { fn(); } } },30); } //获取计算后的样式的值 function getStyle(element, attr) { //能力检测 if(window.getComputedStyle) { return window.getComputedStyle(element, null)[attr]; }else{ return element.currentStyle[attr]; } }
Mysqldump参数大全(参数来源于mysql5.5.19源码)
参数
参数说明
--all-databases , -A
导出全部数据库。
mysqldump -uroot -p --all-databases
--all-tablespaces , -Y
导出全部表空间。
mysqldump -uroot -p --all-databases --all-tablespaces
--no-tablespaces , -y
不导出任何表空间信息。
mysqldump -uroot -p --all-databases --no-tablespaces
--add-drop-database
每个数据库创建之前添加drop数据库语句。
mysqldump -uroot -p --all-databases --add-drop-database
--add-drop-table
每个数据表创建之前添加drop数据表语句。(默认为打开状态,使用--skip-add-drop-table取消选项)
mysqldump -uroot -p --all-databases (默认添加drop语句)
mysqldump -uroot -p --all-databases –skip-add-drop-table (取消drop语句)
--add-locks
在每个表导出之前增加LOCK TABLES并且之后UNLOCK TABLE。(默认为打开状态,使用--skip-add-locks取消选项)
mysqldump -uroot -p --all-databases (默认添加LOCK语句)
mysqldump -uroot -p --all-databases –skip-add-locks (取消LOCK语句)
--allow-keywords
允许创建是关键词的列名字。这由表名前缀于每个列名做到。
mysqldump -uroot -p --all-databases --allow-keywords
--apply-slave-statements
在'CHANGE MASTER'前添加'STOP SLAVE',并且在导出的最后添加'START SLAVE'。
mysqldump -uroot -p --all-databases --apply-slave-statements
--character-sets-dir
字符集文件的目录
mysqldump -uroot -p --all-databases --character-sets-dir=/usr/local/mysql/share/mysql/charsets
--comments
附加注释信息。默认为打开,可以用--skip-comments取消
mysqldump -uroot -p --all-databases (默认记录注释)
mysqldump -uroot -p --all-databases --skip-comments (取消注释)
--compatible
导出的数据将和其它数据库或旧版本的MySQL 相兼容。值可以为ansi、mysql323、mysql40、postgresql、oracle、mssql、db2、maxdb、no_key_options、no_tables_options、no_field_options等,
要使用几个值,用逗号将它们隔开。它并不保证能完全兼容,而是尽量兼容。
mysqldump -uroot -p --all-databases --compatible=ansi
--compact
导出更少的输出信息(用于调试)。去掉注释和头尾等结构。可以使用选项:--skip-add-drop-table --skip-add-locks --skip-comments --skip-disable-keys
mysqldump -uroot -p --all-databases --compact
--complete-insert, -c
使用完整的insert语句(包含列名称)。这么做能提高插入效率,但是可能会受到max_allowed_packet参数的影响而导致插入失败。
mysqldump -uroot -p --all-databases --complete-insert
--compress, -C
在客户端和服务器之间启用压缩传递所有信息
mysqldump -uroot -p --all-databases --compress
--create-options, -a
在CREATE TABLE语句中包括所有MySQL特性选项。(默认为打开状态)
mysqldump -uroot -p --all-databases
--databases, -B
导出几个数据库。参数后面所有名字参量都被看作数据库名。
mysqldump -uroot -p --databases test mysql
--debug
输出debug信息,用于调试。默认值为:d:t:o,/tmp/mysqldump.trace
mysqldump -uroot -p --all-databases --debug
mysqldump -uroot -p --all-databases --debug=” d:t:o,/tmp/debug.trace”
--debug-check
检查内存和打开文件使用说明并退出。
mysqldump -uroot -p --all-databases --debug-check
--debug-info
输出调试信息并退出
mysqldump -uroot -p --all-databases --debug-info
--default-character-set
设置默认字符集,默认值为utf8
mysqldump -uroot -p --all-databases --default-character-set=latin1
--delayed-insert
采用延时插入方式(INSERT DELAYED)导出数据
mysqldump -uroot -p --all-databases --delayed-insert
--delete-master-logs
master备份后删除日志. 这个参数将自动激活--master-data。
mysqldump -uroot -p --all-databases --delete-master-logs
--disable-keys
对于每个表,用/*!40000 ALTER TABLE tbl_name DISABLE KEYS */;和/*!40000 ALTER TABLE tbl_name ENABLE KEYS */;语句引用INSERT语句。这样可以更快地导入dump出来的文件,因为它是在插入所有行后创建索引的。该选项只适合MyISAM表,默认为打开状态。
mysqldump -uroot -p --all-databases
--dump-slave
该选项将导致主的binlog位置和文件名追加到导出数据的文件中。设置为1时,将会以CHANGE MASTER命令输出到数据文件;设置为2时,在命令前增加说明信息。该选项将会打开--lock-all-tables,除非--single-transaction被指定。该选项会自动关闭--lock-tables选项。默认值为0。
mysqldump -uroot -p --all-databases --dump-slave=1
mysqldump -uroot -p --all-databases --dump-slave=2
--events, -E
导出事件。
mysqldump -uroot -p --all-databases --events
--extended-insert, -e
使用具有多个VALUES列的INSERT语法。这样使导出文件更小,并加速导入时的速度。默认为打开状态,使用--skip-extended-insert取消选项。
mysqldump -uroot -p --all-databases
mysqldump -uroot -p --all-databases--skip-extended-insert (取消选项)
--fields-terminated-by
导出文件中忽略给定字段。与--tab选项一起使用,不能用于--databases和--all-databases选项
mysqldump -uroot -p test test --tab=”/home/mysql” --fields-terminated-by=”#”
--fields-enclosed-by
输出文件中的各个字段用给定字符包裹。与--tab选项一起使用,不能用于--databases和--all-databases选项
mysqldump -uroot -p test test --tab=”/home/mysql” --fields-enclosed-by=”#”
--fields-optionally-enclosed-by
输出文件中的各个字段用给定字符选择性包裹。与--tab选项一起使用,不能用于--databases和--all-databases选项
mysqldump -uroot -p test test --tab=”/home/mysql” --fields-enclosed-by=”#” --fields-optionally-enclosed-by =”#”
--fields-escaped-by
输出文件中的各个字段忽略给定字符。与--tab选项一起使用,不能用于--databases和--all-databases选项
mysqldump -uroot -p mysql user --tab=”/home/mysql” --fields-escaped-by=”#”
--flush-logs
开始导出之前刷新日志。
请注意:假如一次导出多个数据库(使用选项--databases或者--all-databases),将会逐个数据库刷新日志。除使用--lock-all-tables或者--master-data外。在这种情况下,日志将会被刷新一次,相应的所以表同时被锁定。因此,如果打算同时导出和刷新日志应该使用--lock-all-tables 或者--master-data 和--flush-logs。
mysqldump -uroot -p --all-databases --flush-logs
--flush-privileges
在导出mysql数据库之后,发出一条FLUSH PRIVILEGES 语句。为了正确恢复,该选项应该用于导出mysql数据库和依赖mysql数据库数据的任何时候。
mysqldump -uroot -p --all-databases --flush-privileges
--force
在导出过程中忽略出现的SQL错误。
mysqldump -uroot -p --all-databases --force
--help
显示帮助信息并退出。
mysqldump --help
--hex-blob
使用十六进制格式导出二进制字符串字段。如果有二进制数据就必须使用该选项。影响到的字段类型有BINARY、VARBINARY、BLOB。
mysqldump -uroot -p --all-databases --hex-blob
--host, -h
需要导出的主机信息
mysqldump -uroot -p --host=localhost --all-databases
--ignore-table
不导出指定表。指定忽略多个表时,需要重复多次,每次一个表。每个表必须同时指定数据库和表名。例如:--ignore-table=database.table1 --ignore-table=database.table2 ……
mysqldump -uroot -p --host=localhost --all-databases --ignore-table=mysql.user
--include-master-host-port
在--dump-slave产生的'CHANGE MASTER TO..'语句中增加'MASTER_HOST=<host>,MASTER_PORT=<port>'
mysqldump -uroot -p --host=localhost --all-databases --include-master-host-port
--insert-ignore
在插入行时使用INSERT IGNORE语句.
mysqldump -uroot -p --host=localhost --all-databases --insert-ignore
--lines-terminated-by
输出文件的每行用给定字符串划分。与--tab选项一起使用,不能用于--databases和--all-databases选项。
mysqldump -uroot -p --host=localhost test test --tab=”/tmp/mysql” --lines-terminated-by=”##”
--lock-all-tables, -x
提交请求锁定所有数据库中的所有表,以保证数据的一致性。这是一个全局读锁,并且自动关闭--single-transaction 和--lock-tables 选项。
mysqldump -uroot -p --host=localhost --all-databases --lock-all-tables
--lock-tables, -l
开始导出前,锁定所有表。用READ LOCAL锁定表以允许MyISAM表并行插入。对于支持事务的表例如InnoDB和BDB,--single-transaction是一个更好的选择,因为它根本不需要锁定表。
请注意当导出多个数据库时,--lock-tables分别为每个数据库锁定表。因此,该选项不能保证导出文件中的表在数据库之间的逻辑一致性。不同数据库表的导出状态可以完全不同。
mysqldump -uroot -p --host=localhost --all-databases --lock-tables
--log-error
附加警告和错误信息到给定文件
mysqldump -uroot -p --host=localhost --all-databases --log-error=/tmp/mysqldump_error_log.err
--master-data
该选项将binlog的位置和文件名追加到输出文件中。如果为1,将会输出CHANGE MASTER 命令;如果为2,输出的CHANGE MASTER命令前添加注释信息。该选项将打开--lock-all-tables 选项,除非--single-transaction也被指定(在这种情况下,全局读锁在开始导出时获得很短的时间;其他内容参考下面的--single-transaction选项)。该选项自动关闭--lock-tables选项。
mysqldump -uroot -p --host=localhost --all-databases --master-data=1;
mysqldump -uroot -p --host=localhost --all-databases --master-data=2;
--max_allowed_packet
服务器发送和接受的最大包长度。
mysqldump -uroot -p --host=localhost --all-databases --max_allowed_packet=10240
--net_buffer_length
TCP/IP和socket连接的缓存大小。
mysqldump -uroot -p --host=localhost --all-databases --net_buffer_length=1024
--no-autocommit
使用autocommit/commit 语句包裹表。
mysqldump -uroot -p --host=localhost --all-databases --no-autocommit
--no-create-db, -n
只导出数据,而不添加CREATE DATABASE 语句。
mysqldump -uroot -p --host=localhost --all-databases --no-create-db
--no-create-info, -t
只导出数据,而不添加CREATE TABLE 语句。
mysqldump -uroot -p --host=localhost --all-databases --no-create-info
--no-data, -d
不导出任何数据,只导出数据库表结构。
mysqldump -uroot -p --host=localhost --all-databases --no-data
--no-set-names, -N
等同于--skip-set-charset
mysqldump -uroot -p --host=localhost --all-databases --no-set-names
--opt
等同于--add-drop-table, --add-locks, --create-options, --quick, --extended-insert, --lock-tables, --set-charset, --disable-keys 该选项默认开启, 可以用--skip-opt禁用.
mysqldump -uroot -p --host=localhost --all-databases --opt
--order-by-primary
如果存在主键,或者第一个唯一键,对每个表的记录进行排序。在导出MyISAM表到InnoDB表时有效,但会使得导出工作花费很长时间。
mysqldump -uroot -p --host=localhost --all-databases --order-by-primary
--password, -p
连接数据库密码
--pipe(windows系统可用)
使用命名管道连接mysql
mysqldump -uroot -p --host=localhost --all-databases --pipe
--port, -P
连接数据库端口号
--protocol
使用的连接协议,包括:tcp, socket, pipe, memory.
mysqldump -uroot -p --host=localhost --all-databases --protocol=tcp
--quick, -q
不缓冲查询,直接导出到标准输出。默认为打开状态,使用--skip-quick取消该选项。
mysqldump -uroot -p --host=localhost --all-databases
mysqldump -uroot -p --host=localhost --all-databases --skip-quick
--quote-names,-Q
使用(`)引起表和列名。默认为打开状态,使用--skip-quote-names取消该选项。
mysqldump -uroot -p --host=localhost --all-databases
mysqldump -uroot -p --host=localhost --all-databases --skip-quote-names
--replace
使用REPLACE INTO 取代INSERT INTO.
mysqldump -uroot -p --host=localhost --all-databases --replace
--result-file, -r
直接输出到指定文件中。该选项应该用在使用回车换行对(\r\n)换行的系统上(例如:DOS,Windows)。该选项确保只有一行被使用。
mysqldump -uroot -p --host=localhost --all-databases --result-file=/tmp/mysqldump_result_file.txt
--routines, -R
导出存储过程以及自定义函数。
mysqldump -uroot -p --host=localhost --all-databases --routines
--set-charset
添加'SET NAMES default_character_set'到输出文件。默认为打开状态,使用--skip-set-charset关闭选项。
mysqldump -uroot -p --host=localhost --all-databases
mysqldump -uroot -p --host=localhost --all-databases --skip-set-charset
--single-transaction
该选项在导出数据之前提交一个BEGIN SQL语句,BEGIN 不会阻塞任何应用程序且能保证导出时数据库的一致性状态。它只适用于多版本存储引擎,仅InnoDB。本选项和--lock-tables 选项是互斥的,因为LOCK TABLES 会使任何挂起的事务隐含提交。要想导出大表的话,应结合使用--quick 选项。
mysqldump -uroot -p --host=localhost --all-databases --single-transaction
--dump-date
将导出时间添加到输出文件中。默认为打开状态,使用--skip-dump-date关闭选项。
mysqldump -uroot -p --host=localhost --all-databases
mysqldump -uroot -p --host=localhost --all-databases --skip-dump-date
--skip-opt
禁用–opt选项.
mysqldump -uroot -p --host=localhost --all-databases --skip-opt
--socket,-S
指定连接mysql的socket文件位置,默认路径/tmp/mysql.sock
mysqldump -uroot -p --host=localhost --all-databases --socket=/tmp/mysqld.sock
--tab,-T
为每个表在给定路径创建tab分割的文本文件。注意:仅仅用于mysqldump和mysqld服务器运行在相同机器上。
mysqldump -uroot -p --host=localhost test test --tab="/home/mysql"
--tables
覆盖--databases (-B)参数,指定需要导出的表名。
mysqldump -uroot -p --host=localhost --databases test --tables test
--triggers
导出触发器。该选项默认启用,用--skip-triggers禁用它。
mysqldump -uroot -p --host=localhost --all-databases --triggers
--tz-utc
在导出顶部设置时区TIME_ZONE='+00:00' ,以保证在不同时区导出的TIMESTAMP 数据或者数据被移动其他时区时的正确性。
mysqldump -uroot -p --host=localhost --all-databases --tz-utc
--user, -u
指定连接的用户名。
--verbose, --v
输出多种平台信息。
--version, -V
输出mysqldump版本信息并退出
--where, -w
只转储给定的WHERE条件选择的记录。请注意如果条件包含命令解释符专用空格或字符,一定要将条件引用起来。
mysqldump -uroot -p --host=localhost --all-databases --where=” user=’root’”
--xml, -X
导出XML格式.
mysqldump -uroot -p --host=localhost --all-databases --xml
--plugin_dir
客户端插件的目录,用于兼容不同的插件版本。
mysqldump -uroot -p --host=localhost --all-databases --plugin_dir=”/usr/local/lib/plugin”
--default_auth
客户端插件默认使用权限。
mysqldump -uroot -p --host=localhost --all-databases --default-auth=”/usr/local/lib/plugin/<PLUGIN>”
PHP CodeIgniter框架源码解析
1.index.php :入口文件
|-->define('ENVIRONMENT') |主要用于设置errors日志输出级别
|-->$system_path |设置系统路径
|-->设置BASEPATH、FCPATH、SYSDIR、APPPATH等 |设置路径信息变量,为加载相应文件信息准备
|-->require_once BASEPATH.core/CodeIgniter.php | 最后加载CodeIgniter.php作为总控制器
2.CodeIgniter.php加载过程,主要用于加载core核心目录下相应文件
|-->require(BASEPATH.'core/Common.php'); |加载core目录下的Common文件,见2.1解析
|-->require(APPPATH.'config/'.ENVIRONMENT.'/constants.php'); |加载constants目录,与开发环境无关时直接使用config目录下的constants目录
|-->get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix'])); |设置子文件,扩展类的前缀
|-->$BM =& load_class('Benchmark', 'core'); |加载benchmark类,mark记录当前的时间
|-->$EXT =& load_class('Hooks', 'core'); |加载core目录下的Hooks钩子类
|-->$EXT->_call_hook('pre_system'); |调用_call_hook(pre_system),根据pre_system内部调用_run_hook执行钩子,在系统开始正式工作前作预处理
|-->$CFG =& load_class('Config', 'core'); |继续执行core下的Config配置文件,
|-->$CFG->_assign_to_config($assign_to_config);
|-->|$this->set_item($key, $val); |解析指定给config的配置文件,实质为对config[]赋值
|-->$UNI =& load_class('Utf8', 'core'); |加载了UTF-8编码类,CI_Utf8
|-->$URI =& load_class('URI', 'core'); |加载core目录的URI类,CI_URI
|-->$RTR =& load_class('Router', 'core'); |设置route路由及覆盖信息,见2.2解析
|-->_set_routing()
|-->_set_overrides()
|-->$OUT =& load_class('Output', 'core'); |实例化输出类,加载core目录下的output文件
|-->$OUT->_display_cache($CFG, $URI) |判断是否存在页面缓存,是则输出文件
|-->$SEC =& load_class('Security', 'core'); |加载core目录下的安全处理文件
|-->$IN =& load_class('Input', 'core'); |实例化输入类,加载core目录下的input文件
|-->$LANG =& load_class('Lang', 'core'); |加载语言类
|-->require BASEPATH.'core/Controller.php'; |加载基本控制器类,见2.3解析
|-->require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'; |尝试加载扩展的自定义子类控制器
|-->include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'); |加载自定义控制器下的控制器类
|-->$BM->mark('loading_time:_base_classes_end'); |设定一个benchmark测试点
|-->$class = $RTR->fetch_class(); |分别获取uri地址的控制器类名和方法名
|-->$method = $RTR->fetch_method();
|-->if ( ! class_exists($class) |判断方法及类是否合理
OR strncmp($method, '_', 1) == 0
OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller')))
)
|-->$EXT->_call_hook('pre_controller'); |处理器执行前进行预处理,并做benchmark设置
|-->$CI = new $class(); |获取执行的控制器实例,实例化构造器
|-->$EXT->_call_hook('post_controller_constructor'); |实例化控制器类后的钩子处理
|-->if (method_exists($CI, '_remap'))
|-->$CI->_remap($method, array_slice($URI->rsegments, 2)) |如果控制器存在_remap()方法,则执行, 判断条件$CI为控制器类
|-->else |判断方法在类当中的存在似,如果不存在,则设置
|-->call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2)); |最终传递参数供调用控制类方法
|-->$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end'); |benchmark标记时间结束点
|-->$EXT->_call_hook('post_controller'); |控制器生存周期,在控制器执行完成后执行后续操作
|-->$OUT->_display(); |输出页面进行展示
|-->$EXT->_call_hook('post_system'); |请求生存周期完成后的终结操作
|-->$CI->db->close(); |自动关闭数据库资源
2.1 Core/Common.php加载
|-->function is_php($version) |用于比较版本号的函数
|-->function is_really_writable($file) |用于判断是否可以写文件,在不同的系统中可靠程度不同,
W中通过判断is_readonly,U中如果safe_mode为开则不确定性
|-->function load_class($class, $directory = 'libraries', $prefix = 'CI_') |用于加载目录下的PHP文件的class类
|-->foreach (array(APPPATH, BASEPATH) as $path) |分别在application和system目录下轮循
|-->file_exists($path.$directory.'/'.$class.'.php' |找到对应的PHP文件
|-->require($path.$directory.'/'.$class.'.php'); |require加载对应的PHP文件内的类,加了前缀,此处可扩展
|-->break; |如正确加载则退出,否则继续尝试加载文件
|-->file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php') |自扩展的class类,如My_Test
|-->if ($name === FALSE) |如果$name不存在,则exit()退出 ,(在自定义类加载时,此处可作为扩展点,增加边际条件)
|-->is_loaded($class); |确类已经加载
|-->$_classes[$class] = new $name(); |加载至静态的classes数祖中,用于缓存,调用时首先从classes中获取
|-->function is_loaded($class = '')
|-->设置$_is_loaded数祖,参数$class不为空,判断是否存在gf $_is_loaded,否则设置
|-->function &get_config($replace = array())|用于获取Config的实例化文件
|-->static $_config; |定义config类型
|-->$file_path = APPPATH.'config/config.php'; |确定application目录路径下定义的config.php的路径
|-->require($file_path); |加载application/config/config.php类
|-->count($replace) > 0 |对于config.php中定义的变量,如果有replace,则逐个替代
|-->foreach ($replace as $key => $val)
|-->$config[$key] = $val;
|-->return $_config[0] =& $config; |最后返回定义的config的结果集
|-->function config_item($item) |配置选项,从config的数祖对象中返还特殊的配置项
|-->$config =& get_config();
|-->$_config_item[$item] = $config[$item];
|-->function show_error |用于错误信息输出
|-->$_error =& load_class('Exceptions', 'core'); |加载Exceptions类
|-->echo $_error->show_error($heading, $message, 'error_general', $status_code); |直接输出错误
|-->function show_404 |用于输出404页面,输出的错误信息页面可配置
|-->function log_message |用于写日志信息
|-->$_log =& load_class('Log');
|-->$_log->write_log($level, $message, $php_error);
|-->function set_status_header |用于输出状态的heade信息
|-->function _exception_handler
|-->function remove_invisible_characters
|-->function html_escape |过滤HTML变量
|-->return htmlspecialchars($var, ENT_QUOTES, config_item('charset'));
2.2Router路由信息设置
|-->_set_routing()
|-->$segments = array() |根据目录,控制器,函数的触发器设定segment[]的uri段值,分别fetch()方法去取对象值
|-->include(APPPATH.'config/routes.php'); |加载config下的routes文件
|-->$this->routes |设置routes数祖值,从config的route中获取
|-->$this->default_controller |设置routes的控制器值,从config的route中获取
|-->return $this->_validate_request($segments); |验证uri的segment合法性
|-->$this->uri->_remove_url_suffix();$this->uri->_reindex_segments(); |进一步清理解析uri,使segment从1开始x
|-->_set_overrides() |根据$routing的值,重新设定directory、controller、function参数
|-->$this->set_directory($routing['directory']);
|-->$this->set_class($routing['controller']);
|-->$this->set_method($routing['function']);
2.3 core/Controller.php加载
|-->__construct() |构造函数
|-->self::$instance =& $this;
|-->foreach (is_loaded() as $var => $class) |根据is_loaded()的信息加载相应的类
|-->$this->$var =& load_class($class);
|-->$this->load =& load_class('Loader', 'core'); |加载core/Loader的php文件
|-->$this->load->initialize(); |主要用于autoload加载信息,如libraries、database等等
|-->function &get_instance |返回当前实例
|-->return self::$instance
扩展点:PHP自动加载机制在CodeIgniter中的应用
1.PHP自动加载机制:PHP5后,提供了类的自动加载机制,即类在加载时才被使用,即Lazy loading,共有二种方式
1.1: __autoload()通过扩展可实现,实质为设定规则加载相应路径的PHP文件(require、include方式)
1.2: 将autoload_func指向php文件,这个一般用c语言扩展实现
详见:http://blog.csdn.net/flyingpig4/article/details/7286438
2.在CodeIgniter中的应用
根据上述源码分析可知:CodeIgniter中所有的操作都是以Controller为起始,只需在Cotroller加载的过程中,
使__autoload()函数自动加载即可,目前的加载方式为在application/config/config.php中设置__autoload()
函数
CI框架源码完全分析之核心文件Codeigniter.php
$assign_to_config['subclass_prefix']));
}
/*
*php 程序运行默认是30s,这里用set_time_limt延长了,关于set_time_Limit() http://www.phpddt.com/php/set_time_limit.html
* 扩展阅读,关于safe_mode:http://www.phpddt.com/php/643.html ,你会完全明白的
*/
if (function_exists("set_time_limit") == TRUE AND @ini_get("safe_mode") == 0)
{
@set_time_limit(300);
}
/*
* 加载Benchmark,它很简单,就是计算任意两点之间程序的运行时间
*/
$BM =& load_class('Benchmark', 'core');
$BM->mark('total_execution_time_start');
$BM->mark('loading_time:_base_classes_start');
//加载钩子,后期会分析到,这玩意特好,扩展它能改变CI的运行流程
$EXT =& load_class('Hooks', 'core');
//这里就是一个钩子啦,其实就是该钩子程序在这里执行
$EXT->_call_hook('pre_system');
//加载配置文件,这里面都是一些加载或获取配置信息的函数
$CFG =& load_class('Config', 'core');
// 如果在index.php中也有配置$assign_to_config,则也把它加入到$CFG
if (isset($assign_to_config))
{
$CFG->_assign_to_config($assign_to_config);
}
//加载utf8组件、URI组件、Router组件
$UNI =& load_class('Utf8', 'core');
$URI =& load_class('URI', 'core');
$RTR =& load_class('Router', 'core');
$RTR->_set_routing();
//如果在index.php中定义了$routing,那么就会覆盖上面路由
if (isset($routing))
{
$RTR->_set_overrides($routing);
}
//加载output输出组件,不然你怎么用$this->Load->view()啊
$OUT =& load_class('Output', 'core');
//又见钩子,这里你可以自己写钩子程序替代Output类的缓存输出
if ($EXT->_call_hook('cache_override') === FALSE)
{
if ($OUT->_display_cache($CFG, $URI) == TRUE)
{
exit;
}
}
//安全组件啦,防xss攻击啊,csrf攻击啊
//关于xss攻击:http://www.phpddt.com/php/php-prevent-xss.html
//关于csrf:攻击:http://www.phpddt.com/reprint/csrf.html
$SEC =& load_class('Security', 'core');
//加载输入组件,就是你常用的$this->input->post();等
$IN =& load_class('Input', 'core');
//加载语言组件啦
$LANG =& load_class('Lang', 'core');
//引入CI的控制器父类
require BASEPATH.'core/Controller.php';
function &get_instance()
{
return CI_Controller::get_instance();
}
//当然你扩展了CI_Controller控制器的话,也要引入啦
if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
{
require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
}
//加载你自己应用中的控制器Controller,如果没有当然error啦
if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'))
{
show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.');
}
include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');
// 好的基础的类都加载完毕了,咱可以mark一下
$BM->mark('loading_time:_base_classes_end');
//路由获取了控制器名和方法名,比如说默认welcome/index
$class = $RTR->fetch_class();
$method = $RTR->fetch_method();
//这里CI规定一般非公共的方法以_开头,下面是判断,如果URI不可访问就show_404()
if ( ! class_exists($class)
OR strncmp($method, '_', 1) == 0
OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller')))
)
{
if ( ! empty($RTR->routes['404_override']))
{
$x = explode('/', $RTR->routes['404_override']);
$class = $x[0];
$method = (isset($x[1]) ? $x[1] : 'index');
if ( ! class_exists($class))
{
if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
{
show_404("{$class}/{$method}");
}
include_once(APPPATH.'controllers/'.$class.'.php');
}
}
else
{
show_404("{$class}/{$method}");
}
}
//又是钩子,该钩子发生在控制器实例化之前的
$EXT->_call_hook('pre_controller');
//又mark一个点
$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');
//终于实例化控制器了
$CI = new $class();
//钩子,不想多说了
$EXT->_call_hook('post_controller_constructor');
/*
* ------------------------------------------------------
* Call the requested method
* ------------------------------------------------------
*/
// Is there a "remap" function? If so, we call it instead
if (method_exists($CI, '_remap'))
{
$CI->_remap($method, array_slice($URI->rsegments, 2));
}
else
{
// is_callable() returns TRUE on some versions of PHP 5 for private and protected
// methods, so we'll use this workaround for consistent behavior
if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI))))
{
// Check and see if we are using a 404 override and use it.
if ( ! empty($RTR->routes['404_override']))
{
$x = explode('/', $RTR->routes['404_override']);
$class = $x[0];
$method = (isset($x[1]) ? $x[1] : 'index');
if ( ! class_exists($class))
{
if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
{
show_404("{$class}/{$method}");
}
include_once(APPPATH.'controllers/'.$class.'.php');
unset($CI);
$CI = new $class();
}
}
else
{
show_404("{$class}/{$method}");
}
}
// 终于调用方法了,$this->load->view()把内容放到缓存区
call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));
}
$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
$EXT->_call_hook('post_controller');
//这里就是把缓存区的内容输出了
if ($EXT->_call_hook('display_override') === FALSE)
{
$OUT->_display();
}
$EXT->_call_hook('post_system');
//关闭数据库的链接
if (class_exists('CI_DB') AND isset($CI->db))
{
$CI->db->close();
}