Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Zend/tests/strict_namespace_bad_value.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
declare(strict_namespace=1) value must be 0 or 1
--FILE--
<?php
declare(strict_namespace=2);
?>
--EXPECTF--
Fatal error: strict_namespace declaration must have 0 or 1 as its value in %s on line %d
9 changes: 9 additions & 0 deletions Zend/tests/strict_namespace_block_mode.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
declare(strict_namespace=1) must not use block mode
--FILE--
<?php
declare(strict_namespace=1) {
}
?>
--EXPECTF--
Fatal error: strict_namespace declaration must not use block mode in %s on line %d
50 changes: 50 additions & 0 deletions Zend/tests/strict_namespace_constants.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
declare(strict_namespace=1) disables global fallback for unqualified constants
--FILE--
<?php
declare(strict_namespace=1);

namespace Foo;

const GREETING = 'foo-greeting'; // Foo\GREETING
\define('BAREWORD', 'global-bareword'); // global constant

// unqualified: resolves to Foo\GREETING
\var_dump(GREETING);

// fully qualified: global constant
\var_dump(\BAREWORD);

// unqualified BAREWORD resolves to Foo\BAREWORD (undefined), no fallback
try {
\var_dump(BAREWORD);
} catch (\Error $e) {
echo $e->getMessage(), "\n";
}

// PHP_INT_MAX resolves to Foo\PHP_INT_MAX; fully qualified reaches the global
try {
\var_dump(PHP_INT_MAX);
} catch (\Error $e) {
echo $e->getMessage(), "\n";
}
\var_dump(\is_int(\PHP_INT_MAX));

// constant expressions follow the same rule
const PREFIX = 'p-';
const COMBINED = PREFIX . 'x';
\var_dump(COMBINED);

// true/false/null are keywords, never namespace-resolved
\var_dump(null, true, false);
?>
--EXPECT--
string(12) "foo-greeting"
string(15) "global-bareword"
Undefined constant "Foo\BAREWORD"
Undefined constant "Foo\PHP_INT_MAX"
bool(true)
string(3) "p-x"
NULL
bool(true)
bool(false)
9 changes: 9 additions & 0 deletions Zend/tests/strict_namespace_not_first.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
declare(strict_namespace=1) must be the first statement in the script
--FILE--
<?php
$x = 1;
declare(strict_namespace=1);
?>
--EXPECTF--
Fatal error: strict_namespace declaration must be the very first statement in the script in %s on line %d
35 changes: 33 additions & 2 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ void zend_file_context_begin(zend_file_context *prev_context) /* {{{ */
FC(in_namespace) = 0;
FC(has_bracketed_namespaces) = 0;
FC(declarables).ticks = 0;
FC(declarables).strict_namespace = 0;
zend_hash_init(&FC(seen_symbols), 8, NULL, NULL, 0);
}
/* }}} */
Expand Down Expand Up @@ -4091,6 +4092,11 @@ static bool zend_compile_function_name(znode *name_node, zend_ast *name_ast) /*
ZVAL_STR(&name_node->u.constant, zend_resolve_function_name(
orig_name, name_ast->attr, &is_fully_qualified));

/* Name is already namespace-prefixed; resolve as qualified, no fallback */
if (FC(declarables).strict_namespace) {
return false;
}

return !is_fully_qualified && FC(current_namespace);
}
/* }}} */
Expand Down Expand Up @@ -7312,6 +7318,27 @@ static void zend_compile_declare(const zend_ast *ast) /* {{{ */
CG(active_op_array)->fn_flags |= ZEND_ACC_STRICT_TYPES;
}

} else if (zend_string_equals_literal_ci(name, "strict_namespace")) {
zval value_zv;

if (FAILURE == zend_is_first_statement(ast, /* allow_nop */ true)) {
zend_error_noreturn(E_COMPILE_ERROR, "strict_namespace declaration must be "
"the very first statement in the script");
}

if (ast->child[1] != NULL) {
zend_error_noreturn(E_COMPILE_ERROR, "strict_namespace declaration must not "
"use block mode");
}

zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false);

if (Z_TYPE(value_zv) != IS_LONG || (Z_LVAL(value_zv) != 0 && Z_LVAL(value_zv) != 1)) {
zend_error_noreturn(E_COMPILE_ERROR, "strict_namespace declaration must have 0 or 1 as its value");
}

FC(declarables).strict_namespace = Z_LVAL(value_zv) == 1;

} else {
zend_error(E_COMPILE_WARNING, "Unsupported declare '%s'", ZSTR_VAL(name));
}
Expand Down Expand Up @@ -11370,7 +11397,9 @@ static void zend_compile_const(znode *result, const zend_ast *ast) /* {{{ */
opline = zend_emit_op_tmp(result, ZEND_FETCH_CONSTANT, NULL, NULL);
opline->op2_type = IS_CONST;

if (is_fully_qualified || !FC(current_namespace)) {
/* strict_namespace disables the fallback; true/false/null were already
* resolved by zend_try_ct_eval_const() above */
if (is_fully_qualified || FC(declarables).strict_namespace || !FC(current_namespace)) {
opline->op1.num = 0;
opline->op2.constant = zend_add_const_name_literal(
resolved_name, false);
Expand Down Expand Up @@ -11747,8 +11776,10 @@ static void zend_compile_const_expr_const(zend_ast **ast_ptr) /* {{{ */
}

zend_ast_destroy(ast);
/* strict_namespace also suppresses the fallback in constant expressions
* (const X = FOO;, default values, attribute arguments) */
*ast_ptr = zend_ast_create_constant(resolved_name,
!is_fully_qualified && FC(current_namespace) ? IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE : 0);
!is_fully_qualified && !FC(declarables).strict_namespace && FC(current_namespace) ? IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE : 0);
}
/* }}} */

Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ static zend_always_inline znode *zend_ast_get_znode(zend_ast *ast) {

typedef struct _zend_declarables {
zend_long ticks;
/* No global fallback for unqualified function/constant names in a namespace */
bool strict_namespace;
} zend_declarables;

/* Compilation context that is different for each file, but shared between op arrays. */
Expand Down
Loading