# Idea: ifdef-like feature to ease userland BC

- Thread: https://externals.io/message/73008
- Original archive: https://news-web.php.net/php.internals/73008
- Started by Marco Schuster on 2014-03-08T21:36:03+00:00
- Messages: 12
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #73008 — Marco Schuster — 2014-03-08T21:36:03+00:00

URL: https://externals.io/message/73008#73008

Hi Internals,

especially with (radical) userland API changes coming up in PHP6,
might I suggest something like a ifdef feature which turns blocks of
code into comments?
This would allow framework authors to provide backwards compatibility
with "older" PHP versions while being able to take advantage of new
PHP features.
In theory, it could also be supplied as "update" to the already
existing minor versions (5.4,5.5 and 5.6) so that these versions can
also use the new code.

---

## #73009 — Andrea Faulds — 2014-03-08T21:40:07+00:00

URL: https://externals.io/message/73008#73009 · In reply to: #73008

> especially with (radical) userland API changes coming up in PHP6,
> might I suggest something like a ifdef feature which turns blocks of
> code into comments?

Why is that needed? PHP checks function names and arguments at runtime, not compile-time. You can already do this:

if (some_condition) {
    PHP_5_6_function();
} else {
    PHP_5_2_function();
}

--
Andrea Faulds
http://ajf.me/

---

## #73010 — Marco Schuster — 2014-03-08T21:48:22+00:00

URL: https://externals.io/message/73008#73010 · In reply to: #73009

Hi,


>
> 
>
> especially with (radical) userland API changes coming up in PHP6,
> might I suggest something like a ifdef feature which turns blocks of
> code into comments?
>
>
> Why is that needed? PHP checks function names and arguments at runtime, not
> compile-time. You can already do this:
>
> if (some_condition) {
>     PHP_5_6_function();
> } else {
>     PHP_5_2_function();
> }
>
This breaks if you use new syntax features like the array shortcut
$var=[];, traits or similar.

Marco

---

## #73011 — me@rouvenwessling.de — 2014-03-08T21:58:35+00:00

URL: https://externals.io/message/73008#73011 · In reply to: #73010

> $var=[];, traits or similar

Neither of these is something you'd really do a version switch around, as it would lead to a lot of duplicated code, that needs to be kept in sync. Of the top of my head there are three situations where branching based on the PHP version is necessary/useful:

1. I'm using an API which is deprecated/unavailable in the newer version I support but the replacement is not available in the older version.

2. I can improve performance, security or functionality by using some new native function that is not available in the newer version. (For example hex2bin only being available in PHP >= 5.4)

3. Working around bugs in specific PHP versions.

It would be incredibly unlikely for any of these to involve recently improved syntax. Also note that from what I'm understanding the OPCache Optimizer will actually remove some branches which can't be called, so there's some chance that there will be no runtime performance impact at all by these checks.

Best regards
Rouven

---

## #73012 — Andrea Faulds — 2014-03-08T22:33:11+00:00

URL: https://externals.io/message/73008#73012 · In reply to: #73010

> This breaks if you use new syntax features like the array shortcut
> $var=[];, traits or similar.


Right, but those aren’t problems. I can’t see anyone willingly writing:

    #ifdef PHP_5_4
    $a = [1,2,3];
    #else
    $a = array(1,2,3);
    #endif

They will simply write:

    $a = array(1,2,3);

For traits, again, it’s unlikely people will want to write two sets of code and deliberately duplicate things. They’ll simply just not use traits.

The only case where what you’re saying makes sense is where features are removed in favour of alternatives, and I can’t see that happening for any syntactic features.

--
Andrea Faulds
http://ajf.me/

---

## #73014 — Chris Wright — 2014-03-08T23:01:28+00:00

URL: https://externals.io/message/73008#73014 · In reply to: #73012

>
> 
> > […]
>
>
> Right, but those aren't problems. I can't see anyone willingly writing:
>
>     #ifdef PHP_5_4
>     $a = [1,2,3];
>     #else
>     $a = array(1,2,3);
>     #endif
>
> They will simply write:
>
>     $a = array(1,2,3);
>
> For traits, again, it's unlikely people will want to write two sets of code and deliberately duplicate things. They'll simply just not use traits.
>
> The only case where what you're saying makes sense is where features are removed in favour of alternatives, and I can't see that happening for any syntactic features.
>
> --
> Andrea Faulds
> http://ajf.me/
>

I'm not necessarily in favour of this idea as a core feature but to
give a more practical example of how this could be useful: generators
can give considerable efficiency gains where previously more resources
would have been consumed by a process yielding values over time.
Before 5.5, generator code would cause a parse error.

Imagine the following somewhat contrived example:

function some_func() {
    $fp = fopen('some_file.ext');
    $result = array();
    while (false !== $line = fgets($fp)) {
        $result[] = $line;
    }
    return $result;
}

foreach (some_func() as $foo) {
    // do stuff
}

Imagine that some_func() is only ever used in the context of a
foreach. For BC reasons, we have to keep this API. Since 5.5, we could
use a generator (yes, we could use an iterator, I said it was
contrived :-P), but we need to provide BC with older PHP versions:

function some_func() {
    $fp = fopen('some_file.ext');
#if `PHP_VERSION` >= 5.5.0
    while (false !== $line = fgets($fp)) {
        yield $line;
    }
#else
    $result = array();
    while (false !== $line = fgets($fp)) {
        $result[] = $line;
    }
    return $result;
#endif
}

As another example, imagine a heavy data processing routine that uses
goto for performance reasons, but before 5.3 would fall back to using
private methods instead:

#if `PHP_VERSION` >= 5.3.0
private function partOfRoutine()
#else
partOfRoutine:
#endif
{
    /* stuff */

#if `PHP_VERSION` >= 5.3.0
    anotherPartOfRoutine();
#else
    goto anotherPartOfRoutine;
#endif
}

Another advantage for performance-minded people is that a
pre-processor is a one-shot deal, whereas runtime checks are
potentially performed a few hundred times a second with the exact same
result every time for ever more.

**However** the thought occurs that, while potentially useful, a
pre-processor of this nature could easily be done with a userland
implementation, a composer dependency and a composer
after-install/after-update script...

Thanks, Chris

---

## #73015 — Andrea Faulds — 2014-03-08T23:33:18+00:00

URL: https://externals.io/message/73008#73015 · In reply to: #73014

> Imagine that some_func() is only ever used in the context of a
> foreach. For BC reasons, we have to keep this API. Since 5.5, we could
> use a generator (yes, we could use an iterator, I said it was
> contrived :-P), but we need to provide BC with older PHP versions:
> 
> function some_func() {
>    $fp = fopen('some_file.ext');
> #if `PHP_VERSION` >= 5.5.0
>    while (false !== $line = fgets($fp)) {
>        yield $line;
>    }
> #else
>    $result = array();
>    while (false !== $line = fgets($fp)) {
>        $result[] = $line;
>    }
>    return $result;
> #endif
> }

Ah, that’s a good example.

> **However** the thought occurs that, while potentially useful, a
> pre-processor of this nature could easily be done with a userland
> implementation, a composer dependency and a composer
> after-install/after-update script…

Right. You could also just use include or require to load a file with a PHP 5.6 implementation or a PHP 5.5 implementation, conditionally, though for differences which are just a few lines, this would be inconvenient.
--
Andrea Faulds
http://ajf.me/

---

## #73016 — Marco Schuster — 2014-03-08T23:33:39+00:00

URL: https://externals.io/message/73008#73016 · In reply to: #73014

Hi Chris,


> I'm not necessarily in favour of this idea as a core feature but to
> give a more practical example of how this could be useful: generators
> can give considerable efficiency gains where previously more resources
> would have been consumed by a process yielding values over time.
> Before 5.5, generator code would cause a parse error.
> [...]
> Another advantage for performance-minded people is that a
> pre-processor is a one-shot deal, whereas runtime checks are
> potentially performed a few hundred times a second with the exact same
> result every time for ever more.
>
> **However** the thought occurs that, while potentially useful, a
> pre-processor of this nature could easily be done with a userland
> implementation, a composer dependency and a composer
> after-install/after-update script...
Yep, I had code like yours in mind when asking.
One problem of doing it with Composer, for example, is that it is not
compatible with stuff like WordPress plugins - I personally would
really like to be able to use "modern" features while still allowing
for all those myriads of version combinations provided by web hosts.

One idea to do this with a "polyfill" could be (expensive)
preprocessing with `file_get_contents()` and eval(), but I think that
step would offset any performance gains by using goto, generators etc.

Marco


-- 
Marco Schuster
IT-Beratung, Web Development
Blütenstr. 23
80799 München

---

## #73017 — Arvids Godjuks — 2014-03-08T23:41:25+00:00

URL: https://externals.io/message/73008#73017 · In reply to: #73016

Hello Internals.

This really sounds strange. Why? Because you already have all the tools in
userland to do all that since like PHP 4.

You have "function_exists"
http://lv.php.net/manual/en/function.function-exists.php , you have
"phpversion"  http://lv.php.net/manual/en/function.phpversion.php, you have
 `PHP_VERSION_ID` constant (details about the constant are in
http://lv.php.net/manual/en/function.phpversion.php too), you have
version_compare http://lv.php.net/manual/en/function.version-compare.php

Sometimes you just need to look up the actual manual...

---

## #73018 — Marco Schuster — 2014-03-08T23:44:54+00:00

URL: https://externals.io/message/73008#73018 · In reply to: #73017

Hi,

On Sun, Mar 9, 2014 at 12:41 AM, Arvids Godjuks
<arvids.godjuks@gmail.com> wrote:
> Hello Internals.
>
> This really sounds strange. Why? Because you already have all the tools in
> userland to do all that since like PHP 4.
>
> You have "function_exists"
> http://lv.php.net/manual/en/function.function-exists.php , you have
> "phpversion"  http://lv.php.net/manual/en/function.phpversion.php, you have
> `PHP_VERSION_ID` constant (details about the constant are in
> http://lv.php.net/manual/en/function.phpversion.php too), you have
> version_compare http://lv.php.net/manual/en/function.version-compare.php
>
> Sometimes you just need to look up the actual manual...

This helps against API-layer changes, but once you try out new syntax
features (not just yield/goto, but I have stuff like named parameters
or that argument unpacking stuff in mind), then you need a parse-time
filter so that older PHP versions do not cough up on syntax errors.

Marco

---

## #73019 — me@rouvenwessling.de — 2014-03-09T00:17:11+00:00

URL: https://externals.io/message/73008#73019 · In reply to: #73018

> This helps against API-layer changes, but once you try out new syntax
> features (not just yield/goto, but I have stuff like named parameters
> or that argument unpacking stuff in mind), then you need a parse-time
> filter so that older PHP versions do not cough up on syntax errors.

Try out? Why do you need to support multiple versions of PHP when testing some new feature?

If you have to support the old version anyhow, what advantage do you gain by shipping the same code twice, just one version a bit prettier?

Why would I want to do

#ifdef version >= 5.6
    function(...$array);
#else
    call_user_func_array('function', $array);
#endif

instead of just

call_user_func_array('function', $array);

I really don't want to come across as hostile, but this seems poorly thought out. We all like to play with the new shiny, but I don't see such a feature benefitting the quality of the code out there.

Best regards
Rouven

---

## #73060 — Julien Pauli — 2014-03-11T10:43:57+00:00

URL: https://externals.io/message/73008#73060

> Rouven Weßling wrote:
>
> > […]
>
> I don't mind coming across 'hostile' ...
> The idea that we need to add additional hacks to get around the fact that
> some 'shiny new features' simply don't sit well in the vast majority of
> legacy code. If there is an existing codebase designed for a previous
> version of PHP, simply hacking in bits that might improve things for some
> later users is just wrong full stop! To get the best out of many new
> features the code needs to be redesigned properly!
>
> If one is designing to support older versions then one simply does not use
> the new features. e_strict requires enough structural changes that
> something like this would not work anyway, so there will be many places
> where even this hack will stop legacy versions of PHP running the code?


I'm not in favour of such thing.

- It adds very complex preprocessor code, making the overall PHP code hard
to read and understand. Leave preprocessor to C.

- OPCache already does many things to try to improve the generated code at
compilation, so that execution OPCodes are optimized depending on the
environment. There still has many ways to improve things in here though.

- The more we add features to support multiple PHP version, the more slow
people will migrate. You want to absolutely use "yield" and pretty nice new
syntax feature of PHP ? Simply drop your support for older PHP versions.
That's what well-known-frameworks did with 5.3 (mainly to benefit from
anonymous functions and namespaces), and that's what they'll have to do for
next PHP versions.

Julien.P
