php源码zend_do_begin_namespace函数详解

version:5.6.21

file:Zend/zend_compile.c

line:7055-7152

void zend_do_begin_namespace(const znode *name, zend_bool with_bracket TSRMLS_DC) /* {{{ */
{
    char *lcname;

    /* handle mixed syntax declaration or nested namespaces */
    if (!CG(has_bracketed_namespaces)) {
        if (CG(current_namespace)) {
            /* previous namespace declarations were unbracketed */
            if (with_bracket) {
                zend_error_noreturn(E_COMPILE_ERROR, "Cannot mix bracketed namespace declarations with unbracketed namespace declarations");
            }
        }
    } else {
        /* previous namespace declarations were bracketed */
        if (!with_bracket) {
            zend_error_noreturn(E_COMPILE_ERROR, "Cannot mix bracketed namespace declarations with unbracketed namespace declarations");
        } else if (CG(current_namespace) || CG(in_namespace)) {
            zend_error_noreturn(E_COMPILE_ERROR, "Namespace declarations cannot be nested");
        }
    }

    if (((!with_bracket && !CG(current_namespace)) || (with_bracket && !CG(has_bracketed_namespaces))) && CG(active_op_array)->last > 0) {
        /* ignore ZEND_EXT_STMT and ZEND_TICKS */
        int num = CG(active_op_array)->last;
        while (num > 0 &&
               (CG(active_op_array)->opcodes[num-1].opcode == ZEND_EXT_STMT ||
                CG(active_op_array)->opcodes[num-1].opcode == ZEND_TICKS)) {
            --num;
        }
        if (num > 0) {
            zend_error_noreturn(E_COMPILE_ERROR, "Namespace declaration statement has to be the very first statement in the script");
        }
    }

    CG(in_namespace) = 1;
    if (with_bracket) {
        CG(has_bracketed_namespaces) = 1;
    }

    if (name) {
        lcname = zend_str_tolower_dup(Z_STRVAL(name->u.constant), Z_STRLEN(name->u.constant));
        if (((Z_STRLEN(name->u.constant) == sizeof("self")-1) &&
              !memcmp(lcname, "self", sizeof("self")-1)) ||
            ((Z_STRLEN(name->u.constant) == sizeof("parent")-1) &&
              !memcmp(lcname, "parent", sizeof("parent")-1))) {
            zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as namespace name", Z_STRVAL(name->u.constant));
        }
        efree(lcname);

        if (CG(current_namespace)) {
            zval_dtor(CG(current_namespace));
        } else {
            ALLOC_ZVAL(CG(current_namespace));
        }
        *CG(current_namespace) = name->u.constant;
    } else {
        if (CG(current_namespace)) {
            zval_dtor(CG(current_namespace));
            FREE_ZVAL(CG(current_namespace));
            CG(current_namespace) = NULL;
        }
    }

    if (CG(current_import)) {
        zend_hash_destroy(CG(current_import));
        efree(CG(current_import));
        CG(current_import) = NULL;
    }

    if (CG(current_import_function)) {
        zend_hash_destroy(CG(current_import_function));
        efree(CG(current_import_function));
        CG(current_import_function) = NULL;
    }

    if (CG(current_import_const)) {
        zend_hash_destroy(CG(current_import_const));
        efree(CG(current_import_const));
        CG(current_import_const) = NULL;
    }

    if (CG(doc_comment)) {
        efree(CG(doc_comment));
        CG(doc_comment) = NULL;
        CG(doc_comment_len) = 0;
    }
}

解析

该函数是在语法解析的时候,编译器扫描到namespace xxx;namespace xxx{};namespace {};三种形式的时候调用

zend_do_begin_namespace(const znode *name, zend_bool with_bracket TSRMLS_DC)的参数name为znode结构的命名空间名称,with_bracket表示是否为括号型的命名空间(1表示带括号)

函数刚开始会判断代码中是否同时用了不带括号和带括号的形式,如果是这样的话,会抛出一个编译类型错误:Cannot mix bracketed namespace declarations with unbracketed namespace declaration

例如

<?php
namespace a;

echo "I belong to namespace a";

namespace b {
    echo "I'm from namespace b";
}

或者命名空间被嵌套使用的话,会抛出一个编译类型错误:Namespace declarations cannot be nested

例如

<?php
namespace b {
    namespace a{
        echo "I belong to namespace a";
    }
}

且命名空间不能为self和parent的任何大小写形式,否则会抛出一个编译类型错误:Cannot use xxx as namespace name

例如

<?php
namespace sElf;

echo "I belong to namespace sElf";

命名空间的错误检查完了以后,就是下面的工作了

如果命名空间是有名称值的,将会把名称存入*CG(current_namespace)中

CG(current_namespace)等价于compile_globals.current_namespace

其他细节不做讲解,请读者自行查看

原文地址:https://www.cnblogs.com/xiaozong/p/6298127.html