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. Python试卷

    3.写一个函数,计算一个给定的日期是该年的第几天. def getday(self,y=None,m=None,d=None): date = datetime(y,m,d) days = date. ...

  2. html lang

    目前,语言的标签表示法的国际标准是RFC 4646,名称是<Tags for Identifying Languages>.简单说,这个文件规定,一种语言的标签应该按照如下方式排列: la ...

  3. 2016022601 - redis入门了解

    今天开始学习redis,先从网页上学习,主要学习地址是:易百中的redis和redis中国网站. 此片章学习来自于自:http://www.yiibai.com/redis/redis_quick_g ...

  4. Android “NetworkOnMainThreadException”出错原因及解决办法

    原因: 不允许在主线程中通讯 方法1:非要在主线程中,当然也可以,这样去处理: StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Build ...

  5. 【网络】 NAT

    NAT(Network Address Translation,网络地址转换)是1994年提出的.当在专用网内部的一些主机本来已经分配到了本地IP地址(即仅在本专用网内使用的专用地址),但现在又想和因 ...

  6. 【BZOJ 1031】[JSOI2007]字符加密Cipher

    Description 喜欢钻研问题的JS 同学,最近又迷上了对加密方法的思考.一天,他突然想出了一种他认为是终极的加密办法:把需要加密的信息排成一圈,显然,它们有很多种不同的读法.例如下图,可以读作 ...

  7. 【POJ2104】kth num

    You are working for Macrohard company in data structures department. After failing your previous tas ...

  8. How to open MS word document from the SharePoint 2010 using Microsoft.Office.Interop.dll

    or this you must change the identity of word component inC:\windows\System32\comexp.mscto be interac ...

  9. spring的三种注解管理器

    1.依赖注入的注解解析器 在配置文件中; * xsd xmlns:context="http://www.springframework.org/schema/context" h ...

  10. 构建第一个Java程序