FAQ
Hello all,

I'd like to propose a new RFC for 5.NEXT:

https://wiki.php.net/rfc/const_scalar_expressions

This allows for defining constant expressions which are resolved at compile
time.

for example:

const FOO = 1 + 1;
static $bar = 1 << 2;
function foo($a = 1 | 2) {}
class foo {
     public $bar = 1 << 2;
}

Thoughts?

Anthony

Search Discussions

  • Christian Stoller at Aug 14, 2013 at 6:17 am

    Hello all,

    I'd like to propose a new RFC for 5.NEXT:

    https://wiki.php.net/rfc/const_scalar_expressions

    This allows for defining constant expressions which are resolved at compile
    time.
    What should that be for?
    const FOO = 1 + 1;
    const BAZ = "HELLO " . "WORLD!";
    Why not just writing

    const FOO = 2;
    const BAZ = "HELLO WORLD!";

    I think it makes code les readable. And if you want to give an important hint for the reader of the code, you can still write comments.


    Best regards
    Christian
  • Sebastian Krebs at Aug 14, 2013 at 6:23 am

    Am 14.08.2013 08:17 schrieb "Christian Stoller" <stoller@leonex.de>:
    Hello all,

    I'd like to propose a new RFC for 5.NEXT:

    https://wiki.php.net/rfc/const_scalar_expressions

    This allows for defining constant expressions which are resolved at
    compile
    time.
    What should that be for?
    const FOO = 1 + 1;
    const BAZ = "HELLO " . "WORLD!";
    Why not just writing

    const FOO = 2;
    const BAZ = "HELLO WORLD!";

    I think it makes code les readable. And if you want to give an important
    hint for the reader of the code, you can still write comments.

    Other examples are better I think

    const $flag3 = 1<<7;
    const $timeout = 60*8;

    For strings it can make sense to avoid newlines, when you want to split a
    long string onto several lines.

    Best regards
    Christian
  • Patrick Schaaf at Aug 14, 2013 at 6:30 am
    Would this allow using constants, too? Class constants?

    const FOO = 1;
    const BAR = self::FOO + 1;
    const BAZ = self::FOO + 2;
    const BARF = GLOBAL_BARF;
    const IMPORT = otherclass::IMPORT; // with autoloading?

    In my opinion these would start to make the feature useful.

    Even more useful, but probably outside the scope of that RFC, would be
    permitting arbitrary expressions in the initializers (const or property
    defaults) that would be resolved at runtime, before the class is used for
    the first time.

    best regards
       Patri
  • Derick Rethans at Aug 14, 2013 at 11:03 am

    On Wed, 14 Aug 2013, Patrick Schaaf wrote:

    Would this allow using constants, too? Class constants?

    const FOO = 1;
    const BAR = self::FOO + 1;
    const BAZ = self::FOO + 2;
    const BARF = GLOBAL_BARF;
    const IMPORT = otherclass::IMPORT; // with autoloading?

    In my opinion these would start to make the feature useful.
    Those are not constants, but expressions which can't be run during
    compile time.

    Derick
  • Patrick Schaaf at Aug 14, 2013 at 11:13 am

    Am 14.08.2013 13:03 schrieb "Derick Rethans" <derick@php.net>:
    On Wed, 14 Aug 2013, Patrick Schaaf wrote:

    Would this allow using constants, too? Class constants?

    const FOO = 1;
    const BAR = self::FOO + 1;
    Those are not constants, but expressions which can't be run during
    compile time.
    Understood now. Constants are not constants.

    Is there a general consensus that just-in-time at-runtime initialization in
    these places (as well as for property initializers) is not possible or
    desired - or is that just something that would need a lot of thought and
    work?

    best regards
       Patrick
  • Johannes Schlüter at Aug 14, 2013 at 2:48 pm

    On Wed, 2013-08-14 at 12:03 +0100, Derick Rethans wrote:
    On Wed, 14 Aug 2013, Patrick Schaaf wrote:

    Would this allow using constants, too? Class constants?

    const FOO = 1;
    const BAR = self::FOO + 1;
    const BAZ = self::FOO + 2;
    const BARF = GLOBAL_BARF;
    const IMPORT = otherclass::IMPORT; // with autoloading?

    In my opinion these would start to make the feature useful.
    Those are not constants, but expressions which can't be run during
    compile time.
    We already support constants where we calculate the value at runtime via
    ZEND_DECLARE_CONST opcode. i.e.
      class A { const C1 = 1; const C2 = A::C1; }
    is valid. If we allow scalar arithmetic we should extend this, too to be
    consistent in the language.

    johannes
  • Pierre Joye at Aug 14, 2013 at 7:37 am

    On Wed, Aug 14, 2013 at 8:17 AM, Christian Stoller wrote:
    Hello all,

    I'd like to propose a new RFC for 5.NEXT:

    https://wiki.php.net/rfc/const_scalar_expressions

    This allows for defining constant expressions which are resolved at compile
    time.
    What should that be for?
    const FOO = 1 + 1;
    const BAZ = "HELLO " . "WORLD!";
    Why not just writing

    const FOO = 2;
    const BAZ = "HELLO WORLD!";

    I think it makes code les readable. And if you want to give an important hint for the reader of the code, you can still write comments.
    When a constant is based on a complex formula, it makes a lot of sense
    to keep it as it is defined or known. I had many projects (finance,
    stats and BI related areas) where I would have used constant
    expressions. As you suggested comments work fine, but do you know what
    my comments were? The expressions used to generate the constant,
    sounds double with little to no gain to me.

    Summary: huge +1 on that.

    --
    Pierre

    @pierrejoye | http://www.libgd.org
  • Julien Pauli at Aug 14, 2013 at 8:15 am

    On Tue, Aug 13, 2013 at 6:12 PM, Anthony Ferrara wrote:

    Hello all,

    I'd like to propose a new RFC for 5.NEXT:

    https://wiki.php.net/rfc/const_scalar_expressions

    This allows for defining constant expressions which are resolved at compile
    time.

    for example:

    const FOO = 1 + 1;
    static $bar = 1 << 2;
    function foo($a = 1 | 2) {}
    class foo {
    public $bar = 1 << 2;
    }

    Thoughts?
    Haha Anthony, so strange you think about this point I was just myself
    figuring out yesterday.

    I'am obviously +1.

    Julien Pauli.
  • Stas Malyshev at Aug 14, 2013 at 9:01 am
    Hi!
    I like the idea, but absence of constant support makes this thing much
    less useful, as you can't do things like:

    public $angle = M_PI/2;

    I think this is one of the reasons this idea was never implemented -
    because without constant support you're limited to doing things that are
    quite obvious and trivial.
    --
    Stanislav Malyshev, Software Architect
    SugarCRM: http://www.sugarcrm.com/
    (408)454-6900 ext. 227
  • Michael Wallner at Aug 14, 2013 at 9:46 am

    On 14 August 2013 11:01, Stas Malyshev wrote:
    Hi!
    I like the idea, but absence of constant support makes this thing much
    less useful, as you can't do things like:

    public $angle = M_PI/2;

    I think this is one of the reasons this idea was never implemented -
    because without constant support you're limited to doing things that are
    quite obvious and trivial.
    Yeah, I'm generally +1 on the idea; constant support would be awesome though!


    --
    Regards,
    Mike
  • Anthony Ferrara at Aug 14, 2013 at 10:44 am
    Stas,

    On Wed, Aug 14, 2013 at 5:01 AM, Stas Malyshev wrote:

    Hi!
    I like the idea, but absence of constant support makes this thing much
    less useful, as you can't do things like:

    public $angle = M_PI/2;

    I think this is one of the reasons this idea was never implemented -
    because without constant support you're limited to doing things that are
    quite obvious and trivial.
    Yeah, having constants in those expressions would be great. If only
    constants in PHP were actually constant...

    But this win is really cheap (a trivial change to the parser), so I figured
    it was worth proposing separately. If we want to add the opcode stream
    later to do expressions for constant values, we can. This just gives us the
    quick win today of allowing relatively trivial, but important expressions.

    The biggest wins I see are in power-of-2 math:

    class Foo {
         const FLAG_1 = 1 << 0;
         const FLAG_2 = 1 << 1;
         const FLAG_3 = 1 << 2;
         const FLAG_4 = 1 << 3;
         const FLAG_5 = 1 << 4;
         const FLAG_6 = 1 << 5;
         const FLAG_7 = 1 << 6;
    }

    And in other complex formulas where having the self-declaration adds
    semantic meaning.

    Now, as far as if it's worth while making the change without constant
    support, that's for each of us to decide. I think it is, but if you don't,
    that's cool too.

    Thanks

    Anthony
  • Julien Pauli at Aug 14, 2013 at 10:52 am

    On Wed, Aug 14, 2013 at 12:44 PM, Anthony Ferrara wrote:

    Stas,

    On Wed, Aug 14, 2013 at 5:01 AM, Stas Malyshev wrote:

    Hi!
    I like the idea, but absence of constant support makes this thing much
    less useful, as you can't do things like:

    public $angle = M_PI/2;

    I think this is one of the reasons this idea was never implemented -
    because without constant support you're limited to doing things that are
    quite obvious and trivial.
    Yeah, having constants in those expressions would be great. If only
    constants in PHP were actually constant...

    But this win is really cheap (a trivial change to the parser), so I
    figured it was worth proposing separately. If we want to add the opcode
    stream later to do expressions for constant values, we can. This just gives
    us the quick win today of allowing relatively trivial, but important
    expressions.

    The biggest wins I see are in power-of-2 math:
    I agree !

    class Foo {
    const FLAG_1 = 1 << 0;
    const FLAG_2 = 1 << 1;
    const FLAG_3 = 1 << 2;
    const FLAG_4 = 1 << 3;
    const FLAG_5 = 1 << 4;
    const FLAG_6 = 1 << 5;
    const FLAG_7 = 1 << 6;
    }
    Julien
  • Sebastian Krebs at Aug 14, 2013 at 11:44 am
    Hi,

    Just asking: Does this cover only declarations, or every constant
    expression, for example

    $weeks = $secs / (60 * 60 * 24 * 7);

    becomes to the opcode-equivalent of

    $weeks = $secs / (604800);

    ?


    2013/8/14 Anthony Ferrara <ircmaxell@gmail.com>
    Stas,


    On Wed, Aug 14, 2013 at 5:01 AM, Stas Malyshev <smalyshev@sugarcrm.com
    wrote:
    Hi!
    I like the idea, but absence of constant support makes this thing much
    less useful, as you can't do things like:

    public $angle = M_PI/2;

    I think this is one of the reasons this idea was never implemented -
    because without constant support you're limited to doing things that are
    quite obvious and trivial.
    Yeah, having constants in those expressions would be great. If only
    constants in PHP were actually constant...

    But this win is really cheap (a trivial change to the parser), so I figured
    it was worth proposing separately. If we want to add the opcode stream
    later to do expressions for constant values, we can. This just gives us the
    quick win today of allowing relatively trivial, but important expressions.

    The biggest wins I see are in power-of-2 math:

    class Foo {
    const FLAG_1 = 1 << 0;
    const FLAG_2 = 1 << 1;
    const FLAG_3 = 1 << 2;
    const FLAG_4 = 1 << 3;
    const FLAG_5 = 1 << 4;
    const FLAG_6 = 1 << 5;
    const FLAG_7 = 1 << 6;
    }

    And in other complex formulas where having the self-declaration adds
    semantic meaning.

    Now, as far as if it's worth while making the change without constant
    support, that's for each of us to decide. I think it is, but if you don't,
    that's cool too.

    Thanks

    Anthony


    --
    github.com/KingCrunch
  • Anthony Ferrara at Aug 14, 2013 at 12:39 pm
    Sebastian,

    On Wed, Aug 14, 2013 at 7:44 AM, Sebastian Krebs wrote:

    Hi,

    Just asking: Does this cover only declarations, or every constant
    expression, for example

    $weeks = $secs / (60 * 60 * 24 * 7);

    becomes to the opcode-equivalent of

    $weeks = $secs / (604800);

    ?
    Currently, only places that use the static_scalar parser definition will
    use this: http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_language_parser.y#945

    So today that's constants (using const keyword), class constants, class
    properties, function parameter values and declare statements.

    To do the other part would likely need to happen in the compiler itself
    (look for all opcodes of type _SPEC_CONST_CONST, and then optimize those
    away). I will experiment a bit, but my gut tells me it will result in a lot
    of parser or compiler complexity to attempt to do that (for which there
    will be so little gain, as the runtime performance is already quite
    fast)... At which point the complexity isn't worth it.

    Anthony
  • Derick Rethans at Aug 14, 2013 at 1:26 pm

    On Wed, 14 Aug 2013, Anthony Ferrara wrote:

    Sebastian,

    On Wed, Aug 14, 2013 at 7:44 AM, Sebastian Krebs wrote:

    Just asking: Does this cover only declarations, or every constant
    expression, for example

    $weeks = $secs / (60 * 60 * 24 * 7);

    becomes to the opcode-equivalent of

    $weeks = $secs / (604800);
    A week isn't always 7 * 24 hours...
    Currently, only places that use the static_scalar parser definition will
    use this: http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_language_parser.y#945

    So today that's constants (using const keyword), class constants, class
    properties, function parameter values and declare statements.

    To do the other part would likely need to happen in the compiler itself
    (look for all opcodes of type _SPEC_CONST_CONST, and then optimize those
    away). I will experiment a bit, but my gut tells me it will result in a lot
    of parser or compiler complexity to attempt to do that (for which there
    will be so little gain, as the runtime performance is already quite
    fast)... At which point the complexity isn't worth it.
    Perhaps something for OpCache's optimizer though, if it doesn't already
    do that...

    cheers,
    Derick

    --
    http://derickrethans.nl | http://xdebug.org
    Like Xdebug? Consider a donation: http://xdebug.org/donate.php
    twitter: @derickr and @xdebug
    Posted with an email client that doesn't mangle email: alpine
  • Stas Malyshev at Aug 14, 2013 at 5:13 pm
    Hi!
    Perhaps something for OpCache's optimizer though, if it doesn't already
    do that...
    The problem with that is once you start to do conversions, things quicky
    go south, as conversions can depend on runtime variables, and people get
    really weird bugs when their expressions are not evaluated in the same
    time always.

    In static context, like with proposed patch, nobody has expectations so
    saying "it all happens compile-time, runtime settings do not apply" is
    fine. But when you already have running code that works certain way,
    changing it may be dangerous.
    --
    Stanislav Malyshev, Software Architect
    SugarCRM: http://www.sugarcrm.com/
    (408)454-6900 ext. 227
  • Sara Golemon at Aug 15, 2013 at 11:29 pm
    I have mixed emotions on the yes/no side of things, but on the "how" side,
    I think we can do this fairly easily.

    During parse, when a non-scalar const expression is encountered, the actual
    binding to the class is deferred, and bytecodes are emitted in the "(main)"
    function. To put it in psuedo-code terms:

    <?php
    class Foo {
       const bar = M_PI / 2;
    }

    Gets turned into:

    <?php
    class Foo { }
    runkit_constant_add('Foo::bar', M_PI / 2);

    Though obv, in reality we wouldn't use runkit_constant_add(), it'd be a new
    opcode which had the same effect. This is basically transparent to opcode
    caches, allows the constant to actually change based on runtime conditions
    (if you were foolish enough to make your constant so non-constant), and
    leaves the syntax used by the user looking like a normal constant
    declaration.

    -Sara
  • Stas Malyshev at Aug 15, 2013 at 11:42 pm
    Hi!
    Though obv, in reality we wouldn't use runkit_constant_add(), it'd be a
    new opcode which had the same effect. This is basically transparent to
    opcode caches, allows the constant to actually change based on runtime
    If the class definition can actually change at runtime, I think it'd
    make it much harder for opcode caches since they can't any longer assume
    class definition can't change at any random place in the code.

    But this is not the most tricky part. The most tricky part is this:


    if(true) return;
    class Foo {
    const halfpie = M_PI/2;
    }

    Now what happens if this is implemented as an opcode? We can't run any
    opcodes past return statement, but Foo is expected to be defined here.

    --
    Stanislav Malyshev, Software Architect
    SugarCRM: http://www.sugarcrm.com/
    (408)454-6900 ext. 227
  • Johannes Schlüter at Aug 15, 2013 at 11:57 pm

    On Thu, 2013-08-15 at 16:42 -0700, Stas Malyshev wrote:
    Hi!
    Though obv, in reality we wouldn't use runkit_constant_add(), it'd be a
    new opcode which had the same effect. This is basically transparent to
    opcode caches, allows the constant to actually change based on runtime
    If the class definition can actually change at runtime, I think it'd
    make it much harder for opcode caches since they can't any longer assume
    class definition can't change at any random place in the code.

    But this is not the most tricky part. The most tricky part is this:


    if(true) return;
    class Foo {
    const halfpie = M_PI/2;
    }

    Now what happens if this is implemented as an opcode? We can't run any
    opcodes past return statement, but Foo is expected to be defined here.
    note that we have this functionality already:

    $ php -r 'class A { const C = FOO; } define("FOO", 23); echo A::C;'
    23

    The first fetch will resolve that.
    If we add "constant scalar expression" this has to work for class
    constants, too. Else we add crazy new inconsistencies to the language.

    johannes
  • Stas Malyshev at Aug 16, 2013 at 3:42 am
    Hi!
    note that we have this functionality already:

    $ php -r 'class A { const C = FOO; } define("FOO", 23); echo A::C;'
    23

    The first fetch will resolve that.
    This is true, but what happens is that "FOO" is stored and then resolved
    on fetch with direct lookup. Now, if C = M_PI/2, what would we store? We
    could theoretically store string "M_PI/2" and somehow "evaluate" it, or
    we could even translate:

    class A { const C = M_PI/2; }

    to:

    class A { const C = __definition_of_A_C; }

    function __define_A_C() { define(__definition_of_A_C, M_PI/2); }

    and when we encounter __definition_of_A_C for the first time we run
    __define_A_C() and take the value of __definition_of_A_C - but I imaging
    that would seriously complicate the code, especially if we think about
    how to handle exceptional situations there...
    --
    Stanislav Malyshev, Software Architect
    SugarCRM: http://www.sugarcrm.com/
    (408)454-6900 ext. 227
  • Clint Priest at Oct 27, 2013 at 2:28 pm

    On 8/15/2013 10:42 PM, Stas Malyshev wrote:
    Hi!
    note that we have this functionality already:

    $ php -r 'class A { const C = FOO; } define("FOO", 23); echo A::C;'
    23

    The first fetch will resolve that.
    This is true, but what happens is that "FOO" is stored and then resolved
    on fetch with direct lookup. Now, if C = M_PI/2, what would we store? We
    could theoretically store string "M_PI/2" and somehow "evaluate" it, or
    we could even translate:

    class A { const C = M_PI/2; }

    to:

    class A { const C = __definition_of_A_C; }

    function __define_A_C() { define(__definition_of_A_C, M_PI/2); }

    and when we encounter __definition_of_A_C for the first time we run
    __define_A_C() and take the value of __definition_of_A_C - but I imaging
    that would seriously complicate the code, especially if we think about
    how to handle exceptional situations there...
    Stas, is there any currently existing standard "Just In Time" construct
    in zend? Something like a Promise?
          http://en.wikipedia.org/wiki/Futures_and_promises

    If there isn't, would it make sense to add this type of feature into the
    core so that these types of needs could be handled with a Promise? The
    late binding aspect is similar in concept to a Promise I believe...

    -Clint
  • Sara Golemon at Aug 16, 2013 at 12:00 am
    if(true) return;
    class Foo {
    const halfpie = M_PI/2;
    }

    Oooh, excellent point. Wave a finger and tell the user not to do that?
    I wasn't going to bring this up, but an intersectional idea I had a few
    years ago.... Static Constructor. Just like an instance constructor, but
    called on initial load.

    class Foo {
       public static __static_construct(/* no args allowed */) {
         /* Run on class load, similar to (main), but without better
    determinance */
       }
    }

    * Reduces the footprint wherein a class's definition *can* change.
    * Solves the early exit problem
    * If exposed to userspace, could provide a nice encapsulation for things
    like stream_wrapper_register() calls.
  • Arpad Ray at Aug 16, 2013 at 12:42 am

    On Fri, Aug 16, 2013 at 1:00 AM, Sara Golemon wrote:

    I wasn't going to bring this up, but an intersectional idea I had a few
    years ago.... Static Constructor. Just like an instance constructor, but
    called on initial load.

    class Foo {
    public static __static_construct(/* no args allowed */) {
    /* Run on class load, similar to (main), but without better
    determinance */
    }
    }

    * Reduces the footprint wherein a class's definition *can* change.
    * Solves the early exit problem
    * If exposed to userspace, could provide a nice encapsulation for things
    like stream_wrapper_register() calls.
    Related - Python metaclasses <3

    Arpad
  • Patrick Schaaf at Aug 16, 2013 at 7:43 am

    Am 16.08.2013 01:42 schrieb "Stas Malyshev" <smalyshev@sugarcrm.com>:
    But this is not the most tricky part. The most tricky part is this:
    if(true) return;
    class Foo {
    const halfpie = M_PI/2;
    }

    Now what happens if this is implemented as an opcode? We can't run any
    opcodes past return statement, but Foo is expected to be defined here.
    Couldn't the opcodes for such behind-the-scenes initialization be generated
    as the first thing of the '(main)' opcode stream, so that they would run
    before it comes to the "if (true) return;" ?

    Once that is in place, make classes have an optional on-first-use opcode
    stream, too:
    - emit calls to that in the file's up-front opcode stream for each locally
    defined class that has them
    - in that per-class initializer stream, expression evaluation even for
    non-constant initializers (protected $foo = anything::whatever()*10;) could
    be put
    - and also a call to the much talked about static class initializer function
    - all in strict definition order, with fallout related to interdependance
    left to confuse class authors :)

    best regards
       Patrick
  • Sebastian Bergmann at Aug 14, 2013 at 8:17 am

    Am 13.08.2013 18:12, schrieb Anthony Ferrara:
    Thoughts?
      +1 :)

    --
    Sebastian Bergmann Co-Founder and Principal Consultant
    http://sebastian-bergmann.de/ http://thePHP.cc/
  • Patrick ALLAERT at Aug 14, 2013 at 9:12 am

    2013/8/13 Anthony Ferrara <ircmaxell@gmail.com>:
    Hello all,

    I'd like to propose a new RFC for 5.NEXT:

    https://wiki.php.net/rfc/const_scalar_expressions

    This allows for defining constant expressions which are resolved at compile
    time.

    for example:

    const FOO = 1 + 1;
    static $bar = 1 << 2;
    function foo($a = 1 | 2) {}
    class foo {
    public $bar = 1 << 2;
    }

    Thoughts?

    Anthony
    Since the RFC specifically avoid the possibility to use non static values: +1

    Cheers,
    Patrick

    --
    Patrick Allaert
    ---
    http://code.google.com/p/peclapm/ - Alternative PHP Monitor
  • Lars Strojny at Aug 14, 2013 at 1:33 pm
    Super cool, thanks!

    Am 13.08.2013 um 18:12 schrieb Anthony Ferrara <ircmaxell@gmail.com>:
    Hello all,

    I'd like to propose a new RFC for 5.NEXT:

    https://wiki.php.net/rfc/const_scalar_expressions

    This allows for defining constant expressions which are resolved at compile
    time.

    for example:

    const FOO = 1 + 1;
    static $bar = 1 << 2;
    function foo($a = 1 | 2) {}
    class foo {
    public $bar = 1 << 2;
    }

    Thoughts?

    Anthony
  • Bob Weinand at Aug 14, 2013 at 2:23 pm

    Am 13.08.2013 um 14:13 schrieb "Anthony Ferrara" <ircmaxell@gmail.com>:

    Hello all,

    I'd like to propose a new RFC for 5.NEXT:

    https://wiki.php.net/rfc/const_scalar_expressions

    This allows for defining constant expressions which are resolved at compile
    time.

    for example:

    const FOO = 1 + 1;
    static $bar = 1 << 2;
    function foo($a = 1 | 2) {}
    class foo {
    public $bar = 1 << 2;
    }

    Thoughts?

    Anthony
    It really would be nice to use constants in these constant expressions.

    I think this would be particulary useful when using bitwise operators. Example:

    const FLAG_A = 1;
    const FLAG_B = 2;
    const FLAG_C = 4;

    With this proposal we would write:
    function func ($arg = 1 | 2 | 4) # we now need to lookup first which constants have values 1, 2 and 4

    But it would be much more understandable if we were able to write:
    function func ($arg = FLAG_A | FLAG_B | FLAG_C)

    Bob Weinand

Related Discussions

People

Translate

site design / logo © 2022 Grokbase