Edit report at https://bugs.php.net/bug.php?id=73608&edit=1
ID: 73608
Comment by: dave at mudsite dot com
Reported by: spam2 at rhsoft dot net
Summary: inconstistent const behavior
Status: Suspended
Type: Feature/Change Request
Package: Scripting Engine problem
PHP Version: 7.0.13
Block user comment: N
Private report: N
New Comment:
Upon looking deeper it seems the problem exists with registered symbols within a file-context. I
don't know why that'd be top-statement. I imagine this may actually be a relic from php4,
or 5.4 or some old version that never changed since we have a runtime function to handle the issue.
Previous Comments:
------------------------------------------------------------------------
[2016-12-14 00:27:51] nikic@php.net
I don't think we have any technical problems with allowing control-flow dependent const
declarations. In fact, the following is legal:
<?php
if (!$cond) return;
const A = 1;
Or, as a more extreme case:
<?php
if ($cond) goto defA1;
else goto defA2;
defA1: const A = 1; goto cont;
defA2: const A = 2;
cont:
This is effectively equivalent to a const declaration inside an if/else block.
In any case, this change (allowing constant declarations outside of top statements) will need an
RFC. As such, I am suspending this issue.
------------------------------------------------------------------------
[2016-12-14 00:13:07] spam2 at rhsoft dot net
that's nitpicking
if i can use const in context of a constant filled with define() which used a variable i can also
use a variable for const - deny that ha sno technical reason - period
------------------------------------------------------------------------
[2016-12-14 00:09:33] dave at mudsite dot com
"how does that change the fact that in case of "if($x == 1)" you have no idea at
compile time if $x will be 1"
Well, you have to take a look to the grammar. It, as well as the compiler, are written pretty
abstractly. The actual grammar of an if() statement is:
T_IF '(' expr ')' statement
You'd then look to expr, and see that it encompasses MANY types of expressions. For example,
function calls are an expression. So we can take your example and rewrite it as such:
$fp = fopen('/dev/urandom');
if (fread($fp, 2) == "no") [
const FOO = 'bar';
}
In your basic example it's simple to mentally think $x is 1, 1 is 1, 1==1 why is this hard?
Well again, it's abstract, so what is the first 2 characters of the filehandle here? At
compile time, we would have no idea, so we would have no clue if that path should be taken or not.
"either my sample would be invalid code and needs to be a fatal/compile/whatever error or you
can use const with variables"
It's not invalid code and needs no error. There's a disjoint between compiling syntax,
and executing operations. The compiler takes your code and compiles it into opcodes that a virtual
machine can execute over. To show you what the VM syntax looks like here's a very simple
example.
<?php
$x = 1;
var_dump($x);
compiled vars: !0 = $x
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
3 0 E > ASSIGN !0, 1
4 1 INIT_FCALL 'var_dump'
2 SEND_VAR !0
3 DO_ICALL
6 4 > RETURN 1
You can see that the compiler has converted the code into a set of 5 operations, using 1 compiled
variable. The first op sets the value of the CV to 1, begin function call, send a variable denoted
by the memory of the compiled-variable(!!), run the call, and return default(1). You can see that
the compiler doesn't know the value of the compiled variable when it creates the opcode sending
the value to the function, rather, it sends the location that stores the value. To get a better
understanding of -why- this is important.
<?php
$x = rand(0, 1);
var_dump($x);
compiled vars: !0 = $x
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
3 0 E > INIT_FCALL 'rand'
1 SEND_VAL 0
2 SEND_VAL 1
3 DO_ICALL $1
4 ASSIGN !0, $1
4 5 INIT_FCALL 'var_dump'
6 SEND_VAR !0
7 DO_ICALL
6 8 > RETURN 1
Here we end up doing a function call to rand, and take the return value of that setting it to the
memory location of !0($x). So we don't know at compile time if a compiled variable contains a
known value or a variable value, since we need to do things quite abstractly.
------------------------------------------------------------------------
[2016-12-13 23:50:55] spam2 at rhsoft dot net
or to make it very clear, the following code wrks just fine with PHP 7.0 and respects if it is
running via CLI or not - in real life - rh_serverurl . MY_PHP_SELF - you have no idea at compile
time which valkue both will have BUT it works
if(PHP_SAPI != 'cli')
{
define('MY_PHP_SELF', $_SERVER['SCRIPT_NAME']);
define('rh_serverurl', PROTOCOL_PREFIX . MY_SERVER_NAME . $rh_port);
}
else
{
define('MY_PHP_SELF', '/' . basename($_SERVER['SCRIPT_NAME']));
define('rh_serverurl', 'http://localhost');
}
const rh_phpself = rh_serverurl . MY_PHP_SELF;
------------------------------------------------------------------------
[2016-12-13 23:47:21] spam2 at rhsoft dot net
how does that change the fact that in case of "if($x == 1)" you have no idea at compile
time if $x will be 1 - either my sample would be invalid code and needs to be a
fatal/compile/whatever error or you can use const with variables
the point is the damned function call for define() which means 15 define() calls are 0.2% runtime of
our complete core-application while you can assign thounsands of variables in the same runtime but
well, they are variables which can be changed and are not visible in every scope without additional
opcodes $GLOBALS or global $foo;
------------------------------------------------------------------------
The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
https://bugs.php.net/bug.php?id=73608
--
Edit this bug report at https://bugs.php.net/bug.php?id=73608&edit=1