0.php代码

<?php
$a='abc';
$b='def';
$c='ghi';
$d='jkl';
$a.=$b.$c.$d;

1.BNF范式(语法规则)

expr_without_variable:
| variable T_CONCAT_EQUAL expr { zend_check_writable_variable(&$); zend_do_end_variable_parse(&$, BP_VAR_RW, TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_CONCAT, &$$, &$, &$ TSRMLS_CC); } | expr '.' expr { zend_do_binary_op(ZEND_CONCAT, &$$, &$, &$ TSRMLS_CC); }

2.生成opcode

void zend_do_binary_assign_op(zend_uchar op, znode *result, const znode *op1, const znode *op2 TSRMLS_DC) /* {{{ */
{
int last_op_number = get_next_op_number(CG(active_op_array));
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); if (last_op_number > ) { //这一段不知道什么意思
zend_op *last_op = &CG(active_op_array)->opcodes[last_op_number-]; switch (last_op->opcode) {
case ZEND_FETCH_OBJ_RW:
last_op->opcode = op;
last_op->extended_value = ZEND_ASSIGN_OBJ; zend_do_op_data(opline, op2 TSRMLS_CC);
SET_UNUSED(opline->result);
GET_NODE(result, last_op->result);
return;
case ZEND_FETCH_DIM_RW:
last_op->opcode = op;
last_op->extended_value = ZEND_ASSIGN_DIM; zend_do_op_data(opline, op2 TSRMLS_CC);
opline->op2.var = get_temporary_variable(CG(active_op_array));
opline->op2_type = IS_VAR;
SET_UNUSED(opline->result);
GET_NODE(result,last_op->result);
return;
default:
break;
}
} opline->opcode = op;
SET_NODE(opline->op1, op1);
SET_NODE(opline->op2, op2);
opline->result_type = IS_VAR;
opline->result.var = get_temporary_variable(CG(active_op_array));
GET_NODE(result, opline->result);
}
#define SET_NODE(target, src) do { \
target ## _type = (src)->op_type; \
if ((src)->op_type == IS_CONST) { \
target.constant = zend_add_literal(CG(active_op_array), &(src)->u.constant TSRMLS_CC); \
} else { \
target = (src)->u.op; \
} \
} while ()

3.执行opcode

static int ZEND_FASTCALL  ZEND_ASSIGN_CONCAT_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
return zend_binary_assign_op_helper_SPEC_CV_TMP(concat_function, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
} static int ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_CV_TMP(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op2, free_op_data2, free_op_data1;
zval **var_ptr;
zval *value; SAVE_OPLINE();
switch (opline->extended_value) {
case ZEND_ASSIGN_OBJ:
..........break;
case ZEND_ASSIGN_DIM: {
.........
}
break;
default:
value = _get_zval_ptr_tmp(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC);
var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_RW(EX_CVs(), opline->op1.var TSRMLS_CC);
/* do nothing */
break;
} if (UNEXPECTED(var_ptr == NULL)) {
zend_error_noreturn(E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets");
} if (UNEXPECTED(*var_ptr == &EG(error_zval))) {
...........
ZEND_VM_NEXT_OPCODE();
} SEPARATE_ZVAL_IF_NOT_REF(var_ptr); if (UNEXPECTED(Z_TYPE_PP(var_ptr) == IS_OBJECT)
&& Z_OBJ_HANDLER_PP(var_ptr, get)
&& Z_OBJ_HANDLER_PP(var_ptr, set)) {
/* proxy object */
//对象的操作
} else {
binary_op(*var_ptr, *var_ptr, value TSRMLS_CC);//故意设置第一个参数与第二个参数一样
} ...........
zval_dtor(free_op2.var);
...........
ZEND_VM_NEXT_OPCODE();
}
typedef int (*binary_op_type)(zval *, zval *, zval * TSRMLS_DC);

 

执行$a.$b时,执行concat_function,第一个参数为一个临时变量(类型为zval), 执行函数后,这个临时变量即保存了concat后的字符串的首地址,然后这个临时变量会放到yyvsp这个数组里,当再移进.$c后,归约成exp.exp,取出这个临时变量作为函数concat_function的第二个参数,$c作为第三个参数 , 执行concat_funcion,将第一个参数的值放回yyvsp

static int ZEND_FASTCALL  ZEND_CONCAT_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE SAVE_OPLINE();
concat_function(&EX_T(opline->result.var).tmp_var,
opline->op1.zv,
_get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op2.var TSRMLS_CC) TSRMLS_CC); CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
ZEND_API int concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
{
zval op1_copy, op2_copy;
int use_copy1 = , use_copy2 = ; if (Z_TYPE_P(op1) != IS_STRING) {
zend_make_printable_zval(op1, &op1_copy, &use_copy1);
}
if (Z_TYPE_P(op2) != IS_STRING) {
zend_make_printable_zval(op2, &op2_copy, &use_copy2);
} if (use_copy1) {
/* We have created a converted copy of op1. Therefore, op1 won't become the result so
* we have to free it.
*/
if (result == op1) {
zval_dtor(op1);
}
op1 = &op1_copy;
}
if (use_copy2) {
op2 = &op2_copy;
}
if (result==op1 && !IS_INTERNED(Z_STRVAL_P(op1))) { /* special case, perform operations on result */
uint res_len = Z_STRLEN_P(op1) + Z_STRLEN_P(op2); if (Z_STRLEN_P(result) < || (int) (Z_STRLEN_P(op1) + Z_STRLEN_P(op2)) < ) {
efree(Z_STRVAL_P(result));
ZVAL_EMPTY_STRING(result);
zend_error(E_ERROR, "String size overflow");
} Z_STRVAL_P(result) = erealloc(Z_STRVAL_P(result), res_len+1);//realloc在紧接原来内存后面,开僻一块内存,如果没有足够内存,重新找一块内存 memcpy(Z_STRVAL_P(result)+Z_STRLEN_P(result), Z_STRVAL_P(op2), Z_STRLEN_P(op2));
Z_STRVAL_P(result)[res_len]=;
Z_STRLEN_P(result) = res_len;
} else {
int length = Z_STRLEN_P(op1) + Z_STRLEN_P(op2);
char *buf = (char *) emalloc(length + );//重新malloc分配内存 memcpy(buf, Z_STRVAL_P(op1), Z_STRLEN_P(op1));
memcpy(buf + Z_STRLEN_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op2));
buf[length] = ;
ZVAL_STRINGL(result, buf, length, );
}
if (use_copy1) {
zval_dtor(op1);
}
if (use_copy2) {
zval_dtor(op2);
}
return SUCCESS;
}

php 连接字符串. ZEND_ASSIGN_CONCAT/ZEND_CONCAT原理的更多相关文章

  1. Windows Azure 网站:应用程序字符串和连接字符串的工作原理

    编辑人员注释:本文章由 Windows Azure 网站团队的首席项目经理 Stefan Schackow 撰写. Windows Azure 网站上有一个方便的功能,即开发人员可将 Azure 中的 ...

  2. 《Entity Framework 6 Recipes》中文翻译系列 (38) ------ 第七章 使用对象服务之动态创建连接字符串和从数据库读取模型

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第七章 使用对象服务 本章篇幅适中,对真实应用中的常见问题提供了切实可行的解决方案. ...

  3. .NET Core 获取数据库上下文实例的方法和配置连接字符串

    目录 .NET Core 获取数据库上下文实例的方法和配置连接字符串 ASP.NET Core 注入 .NET Core 注入 无签名上下文 OnConfigure 配置 有签名上下文构造函数和自己n ...

  4. ASP.NET web.config中的连接字符串

    在ASP.NET的web.config中,可以用两种方式来写连接字符串的配置. <configuration> <appSettings> <add key=" ...

  5. 使用配置文件定义ADO.NET 的连接字符串

    最近一直在学习ADO.NET的相关知识,发现要对数据库操作的地方都要先创建一个连接字符串: string constr ="Data Source=(local);Initial Catal ...

  6. 用SQL Server(T-SQL)获取连接字符串

    一般情况下,C# 连接SQL Server的字符串可以直接按照说明文档直接手动写出来,或者也可以参考大名鼎鼎的connectionstrings手动拼写 但是如果你已经连接到SQL Server也可以 ...

  7. ASP.NET MVC 5 - 创建连接字符串(Connection String)并使用SQL Server LocalDB

    您创建的MovieDBContext类负责处理连接到数据库,并将Movie对象映射到数据库记录的任务中.你可能会问一个问题,如何指定它将连接到数据库? 实际上,确实没有指定要使用的数据库,Entity ...

  8. EF6 Create Different DataContext on runtime(运行时改变连接字符串)

    引言   在使用EF时,有时我们需要在程序运行过程中动态更改EF的连接字符串,但不幸的时EF是否对 ConfigurationManager.RefreshSection("xxx" ...

  9. MySql 连接字符串

    一.MySQL Connector/ODBC 2.50 (MyODBC 2.50)连接方式 1.本地数据库连接Driver={MySQL};Server=localhost;Option=16834; ...

随机推荐

  1. 类似FirePhp的Chrome.php 调试php

    之前一直用firephp来调试php,主要受限Firefox启动太慢,研究了下chromephp; 写了个简单的判断模版: <?php /** * @Author: Klaus * @Date: ...

  2. LeetCode之Single Number以及拓展

    Problem 1:一个数组中有一个数字a只出现一次,其他数字都出现了两次.请找出这个只出现一次的数字? 考察知识点:异或运算 思路:比如数字 b^b = 0     a^0 = a 因此,可以将数组 ...

  3. 自学JAVA总结

    2.在定义常量的时候C语言中定义为const而JAVA中为final3.在JAVA声明成员变量的时候,使用static来定义.4.在JAVA中的boolean类型只包括true和false,但是在C中 ...

  4. seo 优化 仅针对 来拍呀www.laipaiya.com(一)

    加入百度统计代码:http://tongji.baidu.com/ 查看百度统计优化分析->seo建议 对每个页面的meta标签做修改 首页 title :来拍呀 - | 折扣好房你就来拍呀 k ...

  5. Oracle监听器—静态注册

    注册就是将数据库作为一个服务注册到监听程序.客户端不需要知道数据库名和实例名,只需要知道该数据库对外提供的服务名就可以申请连接到数据库.这个服务名可能与实例名一样,也有可能不一样. 注册分: 1. 静 ...

  6. Mysql grant权限管理

    MySQL 赋予用户权限命令的简单格式可概括为: grant 权限 on 数据库对象 to 用户 [identified by '密码'] 最常用的,弄主从同步的时候,给从库的slave用户设置拥有所 ...

  7. 元类metaClass

    metaClass 实现动态改变对象的能力,这点特别像python(metaClass),Python中类(不是元类)的概念借鉴于Smalltalk groovy demo: class Person ...

  8. ubuntu系统使用快捷键打开终端方式总结

    ctrl + alt + T 三键齐下打开虚拟终端 ctrl + alt + F1 (~F6)打开系统终端ctrl + alt + F7 返回图形界面都可以使用exit命令关闭, 不同的是虚拟终端ex ...

  9. Apple移动设备处理器指令集 armv6、armv7、armv7s及arm64-b

    Arm处理器,因为其低功耗和小尺寸而闻名,几乎所有的手机处理器都基于arm,其在嵌入式系统中的应用非常广泛,它的性能在同等功耗产品中也很出色. Armv6.armv7.armv7s.arm64都是ar ...

  10. TWaver3D入门探索——3D拓扑图之人在江湖

    俗话说,有人的地方就有江湖,江湖就是帮派林立错综复杂的关系网.今天我们就来展示这样一个小小的江湖. 故事背景 崇祯末年,民不聊生,烽烟四起-- 江湖都是有背景的,我们的3D江湖也需要一个背景.江湖就是 ...