Re: [PHP-DEV] Array functions with strict comparison

2023-11-14 Thread Andreas Hennings
On Tue, 14 Nov 2023 at 18:49, Robert Landers  wrote:
>
> On Tue, Nov 14, 2023 at 1:39 PM Andreas Hennings  wrote:
> >
> > Hello Robert,
> >
> > On Tue, 14 Nov 2023 at 11:09, Robert Landers  
> > wrote:
> > >
> > > Andreas,
> > >
> > > Just out of curiosity, what is the use case for this? I can't really
> > > think of a practical case where strict checking is needed for these
> > > functions. Usually, you have a really good idea of what is in the
> > > arrays when writing the code and can handle any edge cases (like
> > > nulls, empty strings, etc) long before you reach for these functions.
> >
> > I could ask the reverse question: When do you ever need a non-strict 
> > comparison?
> > I think in most modern php development, you would prefer the strict
> > comparison version simply because it is more simple and predictable.
> >
> > But for real examples.
> > One thing I remember is array_diff($arr, [null]) to remove NULL
> > values, without removing empty strings.
> > Perhaps we could say this is a special case that could be solved in
> > other ways, because we only remove one value.
> >
> > Another thing is when writing reusable general-purpose functions that
> > should work for all arrays.
> > The caller might know the types of the array values, but the developer
> > of the reusable function does not.
> >
> > Another problem is if your arrays contain anything that is not
> > stringable. like objects and arrays.
> >
> > Maybe I will remember other examples that are more practical.
> >
> > Btw, as a general note on strict vs non-strict:
> > In some cases you want a "half strict" comparison, where '5' equals 5,
> > but true does NOT equal '1'.
> > But for now I am happy to focus on pure strict comparison.
> >
> > Andreas
> >
> >
> > >
> > > Robert Landers
> > > Software Engineer
> > > Utrecht NL
> > >
> > > --
> > > PHP Internals - PHP Runtime Development Mailing List
> > > To unsubscribe, visit: https://www.php.net/unsub.php
> > >
>
> Hello Andreas,
>
> > Another problem is if your arrays contain anything that is not
> > stringable. like objects and arrays.
>
> array_udiff() comes to mind here, is there a reason that doesn't work?
> It would allow you to 'half-equals' things as well, if you want.

Yes this would work, with a callback that does strict diff.
But this is going to be much slower than a native array_diff() for large arrays.
Also, array_udiff() is weird in that it it expects a sorting
comparator function rather than a boolean comparator function.

>
> > I could ask the reverse question: When do you ever need a non-strict 
> > comparison?
>
> You actually answer your own question :)
>
> > where '5' equals 5,
>
> One of the most beautiful things about PHP is that null == 0 == false,
> or '5' == 5 == 5.0, or 1 == true == 'hello world', which is so
> incredibly handy in web-dev that to ignore it is inviting bugs.

One problem of == in php and in javascript is that it is not transitive.
And I would argue in most cases it does a lot more casting than we
need, in a way that can be perceived as surprising.

> Headers may or may not be set, form values may or may not be set, JSON
> documents may or may not be missing keys/values, etc. How everything
> is coerced is extremely well documented and very obvious after working
> with PHP for a while.

I imagine a user study would come to a different conclusion than "very obvious".
But ok.

Anyway, time for an RFC.

(I was going to write more, but I should protect this list from my
preaching and arguing)

Andreas

>
> I'm reminded of this principle in PHP, quite often:
>
> > Be conservative in what you do, be liberal in what you accept from others.
>
> Robert Landers
> Software Engineer
> Utrecht NL

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Array functions with strict comparison

2023-11-14 Thread Andreas Hennings
On Tue, 14 Nov 2023 at 02:08, David Gebler  wrote:
>
> On Sun, Nov 12, 2023 at 8:20 PM Andreas Hennings 
> wrote:
>
> > So to me, this alone is an argument to implement this natively.
> > The other argument is that it is kind of sad how the current functions
> > don't behave as one would expect.
>
>
> I'd expect there to be a larger and proportionately increasing performance
> difference between array_diff versus array_udiff with callback or a
> userland array_diff_strict function the larger the datasets you feed in.
> But I'm not sure how common either the use case of diffing arrays of 25,000
> or 250,000 elements might be, or needing this comparison to be strict
> equality. I suspect the use case where both these conditions apply is very
> rare.

The idea is to use the new functions in general-purpose algorithms
without worrying about type coercion or scaling.

>
> But if you want to create an RFC, please go for it. You could add an extra
> parameter to these functions after the input arrays, which was a flag for
> strict comparison. Whether such a thing with a default value of non-strict
> (so not BC breaking) would be considered preferable to new global
> functions, I'm not sure. I'd probably go with new functions but maybe
> someone else will weigh in with their thoughts.

The extra parameter does not really work for array_diff() or
array_intersect(), because these have variadic parameters.
Or at least it would not be "clean".
Technically we could just check if the last parameter is an array or
not, and if not, we use it as a control flag.
But I don't really like it that much, I prefer clean signatures.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Array functions with strict comparison

2023-11-14 Thread Andreas Hennings
Hello Robert,

On Tue, 14 Nov 2023 at 11:09, Robert Landers  wrote:
>
> Andreas,
>
> Just out of curiosity, what is the use case for this? I can't really
> think of a practical case where strict checking is needed for these
> functions. Usually, you have a really good idea of what is in the
> arrays when writing the code and can handle any edge cases (like
> nulls, empty strings, etc) long before you reach for these functions.

I could ask the reverse question: When do you ever need a non-strict comparison?
I think in most modern php development, you would prefer the strict
comparison version simply because it is more simple and predictable.

But for real examples.
One thing I remember is array_diff($arr, [null]) to remove NULL
values, without removing empty strings.
Perhaps we could say this is a special case that could be solved in
other ways, because we only remove one value.

Another thing is when writing reusable general-purpose functions that
should work for all arrays.
The caller might know the types of the array values, but the developer
of the reusable function does not.

Another problem is if your arrays contain anything that is not
stringable. like objects and arrays.

Maybe I will remember other examples that are more practical.

Btw, as a general note on strict vs non-strict:
In some cases you want a "half strict" comparison, where '5' equals 5,
but true does NOT equal '1'.
But for now I am happy to focus on pure strict comparison.

Andreas


>
> Robert Landers
> Software Engineer
> Utrecht NL
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Array functions with strict comparison

2023-11-12 Thread Andreas Hennings
On Sat, 11 Nov 2023 at 20:43, Andreas Hennings  wrote:
>
> Hello David,
>
> On Sat, 11 Nov 2023 at 20:04, David Gebler  wrote:
> >
> > On Sat, Nov 11, 2023 at 6:05 PM Andreas Hennings 
> > wrote:
> >
> > > Hello internals,
> > > I noticed that array functions like array_diff(), array_intersect()
> > > etc use weak comparison.
> > >
> > >
> > That's not quite correct. Using the example of array_diff, the comparison
> > is a strict equality check on a string cast of the values. So
> > array_diff([""], [false]) will indeed be empty
> > but array_diff(["0"],[false]) will return ["0"].
>
> Thanks, good to know!
> So in other words, it is still some kind of weak comparison, but with
> different casting rules than '=='.
> Still this is not desirable in many cases.
>
> >
> > Tbh any use case for whatever array function but with strict comparison is
> > such an easy thing to implement in userland[1] I'm not bothered about
> > supporting it in core. But that's just me. I don't generally like the idea
> > of adding new array_* or str_* functions to the global namespace without
> > very good cause. There is a precedent for it though, in terms of changes
> > which have gone through in PHP 8, such as array_is_list or str_starts_with.
>
>
> I would argue that the strict variants of these functions would be
> about as useful as the non-strict ones.
> Or in my opinion, they would become preferable over the old functions
> for most use cases.
>
> In other words, we could say the old/existing functions should not
> have been added to the language.
> (of course this does not mean we can or should remove them now)
>
> Regarding performance, I measure something like factor 2 for a diff of
> range(0, 500) minus [5], comparing array_diff() vs array_diff_strict()
> as proposed here.
> So for large arrays or repeated calls it does make a difference.

Some more results on this.
With the right array having only one element, i can actually optimize
the userland function to be almost as fast as the native function.
However, if I pump up the right array, the difference becomes quite bad.


function array_diff_userland(array $array1, array $array2 = [], array
...$arrays): array {
if ($arrays) {
// Process additional arrays only when they exist.
$arrays = array_map('array_values', $arrays);
$array2 = array_merge($array2, ...$arrays);
}
// This is actually slower, it seems.
#return array_filter($array1, fn ($value) => !in_array($value,
$array2, TRUE));
$diff = [];

foreach ($array1 as $k => $value) {
// Use non-strict in_array(), to get a fair comparison with
the native function.
if (!in_array($value, $array2)) {
$diff[$k] = $value;
}
}
return $diff;
}

$arr = range(0, 500);
$arr2 = range(0, 1500, 2);

$dts = [];

$t = microtime(TRUE);
$diff_native = array_diff_userland($arr, $arr2);
$t += $dts['userland'] = (microtime(TRUE) - $t);
$diff_userland = array_diff($arr, $arr2);
$t += $dts['native'] = (microtime(TRUE) - $t);
assert($diff_userland === $diff_native);

// Run both again to detect differences due to warm-up.
$t = microtime(TRUE);
$diff_native = array_diff_userland($arr, $arr2);
$t += $dts['userland.1'] = (microtime(TRUE) - $t);
$diff_userland = array_diff($arr, $arr2);
$t += $dts['native.1'] = (microtime(TRUE) - $t);
assert($diff_userland === $diff_native);

// Now use a right array that has no overlap with the left array.
$t = microtime(TRUE);
$arr2 = range(501, 1500, 2);
$diff_native = array_diff_userland($arr, $arr2);
$t += $dts['userland.2'] = (microtime(TRUE) - $t);
$diff_userland = array_diff($arr, $arr2);
$t += $dts['native.2'] = (microtime(TRUE) - $t);
assert($diff_userland === $diff_native);

var_export(array_map(fn ($dt) => $dt * 1000 * 1000 . ' ns', $dts));

I see differences of factor 5 up to factor 10.

So to me, this alone is an argument to implement this natively.
The other argument is that it is kind of sad how the current functions
don't behave as one would expect.


>
> Regarding the cost of more native functions:
> Is the concern more about polluting the global namespace, or about
> adding more functions that need to be maintained?
> I can see both arguments, but I don't have a clear opinion how these
> costs should be weighed.

The most straightforward option seems to just name the new functions
like array_diff_strict() etc.
But I am happy for other proposals.

>
> Cheers
> Andreas
>
>
>
> >
> > [1] Example:
> >
> > function array_diff_strict(array $array1, array ...$arrays): array
> > {
> > $diff = [];
> > foreach ($array1 as $value) {
> > $found = false;
> &

Re: [PHP-DEV] Array functions with strict comparison

2023-11-11 Thread Andreas Hennings
Hello David,

On Sat, 11 Nov 2023 at 20:04, David Gebler  wrote:
>
> On Sat, Nov 11, 2023 at 6:05 PM Andreas Hennings 
> wrote:
>
> > Hello internals,
> > I noticed that array functions like array_diff(), array_intersect()
> > etc use weak comparison.
> >
> >
> That's not quite correct. Using the example of array_diff, the comparison
> is a strict equality check on a string cast of the values. So
> array_diff([""], [false]) will indeed be empty
> but array_diff(["0"],[false]) will return ["0"].

Thanks, good to know!
So in other words, it is still some kind of weak comparison, but with
different casting rules than '=='.
Still this is not desirable in many cases.

>
> Tbh any use case for whatever array function but with strict comparison is
> such an easy thing to implement in userland[1] I'm not bothered about
> supporting it in core. But that's just me. I don't generally like the idea
> of adding new array_* or str_* functions to the global namespace without
> very good cause. There is a precedent for it though, in terms of changes
> which have gone through in PHP 8, such as array_is_list or str_starts_with.


I would argue that the strict variants of these functions would be
about as useful as the non-strict ones.
Or in my opinion, they would become preferable over the old functions
for most use cases.

In other words, we could say the old/existing functions should not
have been added to the language.
(of course this does not mean we can or should remove them now)

Regarding performance, I measure something like factor 2 for a diff of
range(0, 500) minus [5], comparing array_diff() vs array_diff_strict()
as proposed here.
So for large arrays or repeated calls it does make a difference.

Regarding the cost of more native functions:
Is the concern more about polluting the global namespace, or about
adding more functions that need to be maintained?
I can see both arguments, but I don't have a clear opinion how these
costs should be weighed.

Cheers
Andreas



>
> [1] Example:
>
> function array_diff_strict(array $array1, array ...$arrays): array
> {
> $diff = [];
> foreach ($array1 as $value) {
> $found = false;
> foreach ($arrays as $array) {
> if (in_array($value, $array, true)) {
> $found = true;
> break;
> }
> }
> if (!$found) {
> $diff[] = $value;
> }
> }
> return $diff;
> }

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Array functions with strict comparison

2023-11-11 Thread Andreas Hennings
Hello internals,
I noticed that array functions like array_diff(), array_intersect()
etc use weak comparison.

E.g.
array_diff([0, '', false, null], [null])
only leaves [0].

This makes these functions useless for a number of applications.
Also it can lead to unpleasant surprises, if a developer is not aware
of the silent type casting.

For BC reasons. the existing functions have to remain as they are.
Also, most of them don't really have a place for an extra parameter to
tell them to use strict comparison.

So, as a solution, we would have to introduce new functions.

Has anything like this been proposed in the past?
How would we name the new functions?
Should they be functions or static methods like StrictArray::diff(..)?

I could post an RFC but I want to get some feedback first.

Kind regards
Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Expression code blocks

2023-06-22 Thread Andreas Hennings
Hello Ilija,

On Sat, 17 Jun 2023 at 13:27, Ilija Tovilo  wrote:
>
> Hi Andreas
>
> On Fri, Jun 16, 2023 at 9:23 PM Andreas Hennings  wrote:
> >
> > Hello list,
> > I don't know if something like this was already proposed in the past,
> > I did not find anything.
> >
> > Sometimes it would be nice to have a code block inside an expression, like 
> > this:
> >
> > public function f(string $key) {
> > return $this->cache[$key] ??= {
> > // Calculate a value for $key.
> > [...]
> > return $value;
> > }
> > }
>
> This has been discussed a few years back when match expressions were
> proposed. I originally wanted to include support for code blocks along
> with expressions to offer a more complete alternative to switch
> statements. The other major use-case for block expressions are arrow
> functions.
>
> Unfortunately, a general solution seems suboptimal due to the subtle
> semantic differences. See this message for my detailed thought
> process.
>
> https://externals.io/message/109941#109947

I looked at this and I think all of this can be solved.

About ambiguity with existing code blocks:
I think we should prepend a new keyword like "expr".
I was a bit hesitant to introduce a new keyword, but I think it is the
cleanest solution.
So it becomes `$x = expr {return 5;};`.

For the return syntax, let's simply use `return`, instead of a new
language construct like "<=".

About return being required or not:
Let's do the same as in a regular function body:
- If return value is omitted, an implicit NULL is returned.
- IDEs / static analysis can look at the wider context and complain if
it thinks a return value should be added or omitted. PHP can simply
ignore the problem.

This means that the following will be true:
assert(NULL === expr {});
assert(NULL === expr {return;});
assert(NULL === expr {return NULL;});


>
> I believe it would be best to address blocks for match arms and arrow
> functions separately.

The new syntax could be used in all 3 cases:

$y = match ($x) {
  1 => expr {},  // IDE complains, but PHP does not care.
}

$fn = fn () => expr {};  // Totally ok.
$fn = fn (): array => expr {}; // Error at runtime when it is executed.
$fn = fn (bool $arg): array => expr {if ($arg) return [];};  //
Possible error at runtime when it is executed.

We could also declare a return type on the expr, like so:
But I don't see a big advantage.

$x = expr: array {return [];};


We can also use generators:

$generator = expr {yield 'A'; yield 'B';};
assert($generator instanceof \Generator);


>
> I don't believe blocks for general expressions are that useful in PHP
> due to the lack of block scoping.

We could introduce block scoping for that specific language feature.
And even without it, to me these expression blocks would still be useful.

If the default is "use all by ref, leak all", it would feel more like
other language constructs like foreach(), if/else etc.
If the default is "capture all by value", it would feel more like a
short closure.

Perhaps there could be something to control the capturing type.
E.g. a `use` part?
expr {..}  // Capture all by value, don't leak inner variables.
expr use () {..}  // Capture nothing.
expr use ($x) {..}  // Capture specific variable $x, by value.
expr use (*) {..}  // Capture all by value, don't leak.
expr use (&*) {..}  // Capture all by reference, leak all?

Or we could have different keywords:

expr {..} // Capture all by reference, leak all inner variables.
scope {..} // Capture all by value, don't leak.


I personally think mostly of uses cases with cache and ??=.
These exist a lot within Drupal, both with runtime cache and persistent cache.
With the expression block these could be shortened like this:

$x = $runtime_cache['x']
  ??= $persistent_cache['x']
  ??= {
... // Calculate value for x.
return $value;
  };

I could also see it used in generated code that behaves as a huge
nested expression.



> Your suggestion to make the block a
> separate closure could avoid that (as well as the optimizer issue
> mentioned below) but comes with new issues, like making modification
> of captured values impossible without by-ref capturing. It seems
> confusing that fn {} is auto-executed while fn() {} isn't, as the
> former looks like a shortened version of the latter. fn() => fn {}
> would also look quite weird. match ($x) { 1 => fn {} } seems ok,
> except for being somewhat lengthy.
>
> On another note, the vote for blocks in short closures has failed
> lately (https://wiki.php.net/rfc/auto-capture-closure).
>
> The message above also addresses the syntax ambiguity you mentioned.
> The {} syntax would be unambiguous in the most useful contexts (e.g.
> function para

Re: [PHP-DEV] Expression code blocks

2023-06-22 Thread Andreas Hennings
Hello Rowan, Ilja,
(sorry I did not see these replies sooner, I do some forwarding but it failed)

On Sat, 17 Jun 2023 at 16:59, Rowan Tommins  wrote:
>
> On 17/06/2023 12:26, Ilija Tovilo wrote:
> > I don't believe blocks for general expressions are that useful in PHP
> > due to the lack of block scoping. Your suggestion to make the block a
> > separate closure could avoid that (as well as the optimizer issue
> > mentioned below) but comes with new issues, like making modification
> > of captured values impossible without by-ref capturing.
>
>
> I've been pondering various things in this space for a while,
> particularly since the last auto-capture closures RFC. I haven't quite
> coalesced on a coherent concept, but there are a few things that I think
> inter-relate:
>
> * Opting into block-scoped variables, while retaining full access to the
> outer scope, like JS "let"
> * Code blocks as some kind of first-class thing, distinct from closures

I think this would be my preferred option.
We would have to discuss how variables are scoped and captured, but I
could myself being happy with different options.

Currently variables from outside in short closures are auto-captured
by value, and nothing leaks outside:
https://3v4l.org/mdJvM
I would be happy if it works the same for expression code blocks.

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Expression code blocks

2023-06-16 Thread Andreas Hennings
Hello list,
I don't know if something like this was already proposed in the past,
I did not find anything.

Sometimes it would be nice to have a code block inside an expression, like this:

public function f(string $key) {
return $this->cache[$key] ??= {
// Calculate a value for $key.
[...]
return $value;
}
}

Currently, to achieve the same, we have two options which both add
overhead both in code verbosity and in performance:
1. Call another method or closure after the ??=.
2. Use if (isset(...)) instead of the ??= shortcut. This results in
repetition of other parts of the expression, because the if/else
cannot be inside the expression.

E.g. this is option 1 with a closure:

public function f(string $key) {
return $this->cache[$key] ??= (function () use ($key) {
// Calculate a value for $key.
[...]
return $value;
})();
}

Option 1 with a real method would look like this:

public function f(string $key) {
return $this->cache[$key] ??= $this->calc($key);
}
private function calc(string $key) {
// Calculate a value for $key.
[...]
return $value;
}


The `{}` syntax seems like the most obvious choice at first, but I
think it would not work.

Statement groups with curly brackets are already possible right now,
see https://www.php.net/manual/en/control-structures.intro.php, but
they are not really useful for anything: They cannot be used as
expressions, they don't have their own return value, and they don't
isolate their variables.

Another option would be a special keyword before the curly block.
We could introduce a new keyword like `expr`, or use an existing one like `fn`.

$x = 4 + fn {return 3;};
// or
$x = 4 + expr {return 3;}

The compiler/interpreter could either convert the block into a
closure, or treat it as a new language feature, which might bring
performance benefits.


Any thoughts?
-- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Declaration-aware attributes

2023-05-30 Thread Andreas Hennings
On Tue, 30 May 2023 at 22:45, Larry Garfield  wrote:
>
> On Tue, May 30, 2023, at 7:34 PM, Andreas Hennings wrote:
> > On Tue, 30 May 2023 at 19:12, Larry Garfield  wrote:
> >>
> >> I've run into this issue in my attribute library as well 
> >> (https://github.com/Crell/AttributeUtils).  What I do there is allow 
> >> attributes to opt-in to a callback method via interface.  For example:
> >>
> >> #[\Attribute]
> >> class AttribWithName implements FromReflectionClass
> >> {
> >> public readonly string $name;
> >>
> >> public function __construct(?string $name = null)
> >> {
> >> if ($name) {
> >> $this->name = $name;
> >> }
> >> }
> >>
> >> public function fromReflection(\ReflectionClass $subject): void
> >> {
> >> $this->name ??= $subject->getShortName();
> >> }
> >> }
> >
> > So it is technically from outside it is a setter, whereas from inside
> > it is not really.
> > Technically, this means you need a version of the attribute object
> > that can exist without the values from the reflector.
> > The constructor needs to initialize some "stub" values, until the
> > setter is called.
> > Every other method also needs to support the case when the setter has
> > not been called yet, or possibly throw an exception.
> > Also, your property type has to allow null, so `?string`, not `string`.
>
> Except the property type is not null above?  The argument is, but not the 
> property.  (I could use CPP here with a null value instead, if we had 
> asymmetric visibility.)

True!
My conception of readonly was distorted by the same static analysis
tools that you complained about earlier!
https://3v4l.org/CUgYv
I think a more accurate label would be "write once".

>
> Also, if the interface is called by the reflection system itself as part of 
> getInstance() then yes, we can guarantee that it's been run, just like we can 
> guarantee the constructor has run.

The concern was that you can also construct the attribute object with
a regular `new AttribWithName()` expression, and then you can omit the
->fromReflection() call.
But we could say it is part of the contract that you have to call the
method before the object is fully operational.

>
> >> (Side note: This is why static analysis tools that forbid writing to 
> >> readonly properties except from the constructor are wrong; it's also an 
> >> example of where asymmetric visibility would be superior to readonly.  But 
> >> I digress.)
> >
> > Technically there is no guarantee that the setters will be called
> > before any other method, and only once.
> > If these methods can write on readonly properties, then any method can.
> > Unless we somehow mark these methods as special.
>
> There's nothing special about readonly properties there.  An uninitialized 
> non-readonly property is no more or less susceptible to still being 
> uninitialized when you need it.  Singling out readonly here is just dumb, and 
> exacerbates the problems of readonly.
>
> > On the other hand, a wither method with "clone with" should be allowed
> > to work on readonly properties.
> > You could rewrite your method like this, once we have clone with:
> > (or use the old-school syntax but without readonly)
> >
> > public function withReflector(\ReflectionClass $subject): static
> > {
> > return ($this->name !== NULL)
> > ? $this
> > : clone $this with (name: $subject->getShortName();
> > }
> >
> > Then in the discovery code:
> >
> > $attribute = $reflectionClass->getAttributes()[0]->newInstance();
> > $attribute = $attribute->withReflector($reflectionClass);
>
> In that library I actually have several opt-in post-constructor setup 
> routines.  The documentation covers them all.  Making them all withers would 
> be just needlessly slow.  Basically it's an example of a "mutable and then 
> locked" object, which I emulate by virtue of only calling the setup methods 
> from the library, and using readonly properties.
>
> >> That's why I think an opt-in interface is the way to go.  It also avoids 
> >> any confusion around the constructor parameters, which is, based on this 
> >> thread, a confusing area. :-)
> >
> > What do you think about a placeholder syntax to avoid confusion with a
> > skipped parameter?
> > Like
> >
> > #[A('x', ?', 'y')]
>
> Oh g

Re: [PHP-DEV] Declaration-aware attributes

2023-05-30 Thread Andreas Hennings
On Tue, 30 May 2023 at 19:12, Larry Garfield  wrote:
>
> I've run into this issue in my attribute library as well 
> (https://github.com/Crell/AttributeUtils).  What I do there is allow 
> attributes to opt-in to a callback method via interface.  For example:
>
> #[\Attribute]
> class AttribWithName implements FromReflectionClass
> {
> public readonly string $name;
>
> public function __construct(?string $name = null)
> {
> if ($name) {
> $this->name = $name;
> }
> }
>
> public function fromReflection(\ReflectionClass $subject): void
> {
> $this->name ??= $subject->getShortName();
> }
> }

So it is technically from outside it is a setter, whereas from inside
it is not really.
Technically, this means you need a version of the attribute object
that can exist without the values from the reflector.
The constructor needs to initialize some "stub" values, until the
setter is called.
Every other method also needs to support the case when the setter has
not been called yet, or possibly throw an exception.
Also, your property type has to allow null, so `?string`, not `string`.

I usually try to avoid this, this is why I proposed the constructor parameter.
This way, the object is never in an incomplete state.

(Side note: Personally I use the naming convention from*() for static
factory methods. I might use set*() for this one, but then again, it
is only a setter from the outside.)

>
> (Side note: This is why static analysis tools that forbid writing to readonly 
> properties except from the constructor are wrong; it's also an example of 
> where asymmetric visibility would be superior to readonly.  But I digress.)

Technically there is no guarantee that the setters will be called
before any other method, and only once.
If these methods can write on readonly properties, then any method can.
Unless we somehow mark these methods as special.

On the other hand, a wither method with "clone with" should be allowed
to work on readonly properties.
You could rewrite your method like this, once we have clone with:
(or use the old-school syntax but without readonly)

public function withReflector(\ReflectionClass $subject): static
{
return ($this->name !== NULL)
? $this
: clone $this with (name: $subject->getShortName();
}

Then in the discovery code:

$attribute = $reflectionClass->getAttributes()[0]->newInstance();
$attribute = $attribute->withReflector($reflectionClass);

>
> My preference would be for something along those lines to be implemented in 
> core.
>
> Importantly, we *MUST NOT* design it such that the reflection object gets 
> assigned to a property of the attribute.  Reflection objects are not 
> serializable.  Attributes will frequently be cached.  That means it forces 
> the attribute author to make the property nullable AND then unset it sometime 
> before the attribute gets serialized, or it will break.  That's a no-go.

There could be ways around this problem, but I agree we should avoid
it on design level.

>
> That's why I think an opt-in interface is the way to go.  It also avoids any 
> confusion around the constructor parameters, which is, based on this thread, 
> a confusing area. :-)

What do you think about a placeholder syntax to avoid confusion with a
skipped parameter?
Like

#[A('x', ?', 'y')]

>
> My second preference would be the ReflectionAttribute::inProgress() call in 
> the constructor, or something like that.  I like that less because it's a 
> stateful call, but it would also reduce the issue with readonly properties 
> (as in the example above) by making both alternatives available in the 
> constructor, so maybe it's an acceptable tradeoff.

I would like to avoid anything that is stateful or that leaves an
incomplete stub object.
(But I think I made this clear enough, so..)

>
> I can see an argument either direction here.
>
> --Larry Garfield
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Let ReflectionMethod keep track of original class

2023-05-30 Thread Andreas Hennings
On Tue, 30 May 2023 at 18:48, Larry Garfield  wrote:
>
>
>
> --
>   Larry Garfield
>   la...@garfieldtech.com
>
> On Tue, May 30, 2023, at 2:42 AM, Andreas Hennings wrote:
> > Hello list,
> > this proposal will be useful in combination with "Declaration-aware 
> > attributes"
> >
> >
> > Problem
> > 
> > Currently, ReflectionMethod is not aware of the original class, if the
> > method is declared in a parent class.
> > Methods that are called during a discovery algorithm that need to
> > process a method with its original class typically need two
> > parameters:
> >
> > function processMethod(\ReflectionClass $class, \ReflectionMethod $method) 
> > {..}
> >
> >
> > Proposal
> > 
> > Let a ReflectionMethod object keep track of the original class.
> > Introduce a new method ReflectionMethod->getOriginalClass() to retrieve it.
> >
> > class B {
> >   function f($x) {}
> > }
> > class C extends B {}
> >
> > foreach ([
> >   // There are different ways to get a reflection method object, all
> > of them track the original class.
> >   new ReflectionMethod('C', 'f'),
> >   (new ReflectionClass('C'))->getMethod('f'),
> >   (new ReflectionMethod('C', 
> > 'f'))->getParameters()[0]->getDeclaringFunction(),
> > ] as $rm) {
> >   // The following won't change:
> >   assert($rm->class === 'B');
> >   assert($rm->getDeclaringClass()->getName() === 'B');
> >   // New method:
> >   assert($rm->getOriginalClass()->getName() === 'C');
> >
> >
> > Alternatives
> > ==
> >
> > At first I thought we might introduce a new class like
> > "VirtualReflectionMethod" which behaves as if the method was declared
> > on the child class. But this is awkward.
> >
> > I think the ->getOriginalClass() is much simpler.
>
>
> I would not be opposed to this.  I don't know that I have any use cases for 
> it, but I'd be open to it.

You can search in your favourite project's /vendor/ directory for
methods with two or more parameters that receive reflector objects.

"function .*\(.*Reflection\w+ \$\w+, .?Reflection\w+ \$\w+"

>
> --Larry Garfield
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] [Discussion] Add new function `array_group`

2023-05-30 Thread Andreas Hennings
On Tue, 30 May 2023 at 18:27, Boro Sitnikovski  wrote:
>
> Hi,
>
> Thank you for your thoughts.
>
> > I would say the more common desired behavior is the one in your first
> > example. And even for that we don't have a native function.
>
> This Google search might give more insight into the number of discussions 
> about a grouping functionality: 
> https://www.google.com/search?q=php+group+elements+site:stackoverflow.com

All of the examples I looked at are asking for the first kind of
grouping, that can be implemented as in your first example.
In all the examples, if two items are equal, they end up in the same group.

In your proposed behavior, equal items can end up in distinct groups
depending on their original position in the source array.
I don't see any questions or examples that ask for this.

-- Andreas

>
> > Your behavior can be implemented in userland like so:
> > https://3v4l.org/epvHm
>
> Correct, but then again, we can also implement 
> `array_map`/`array_filter`/etc. in userland :)
>
> > I think you need to make a case as to why the behavior you describe
> > justifies a native function.
>
> Similar to my previous answer, but also in general - ease of access and also 
> performance.
>
> > E.g. if you find a lot of public php code that does this kind of grouping.
> >
> > I personally suspect it is not that common.
> >
> > Cheers
> > Andreas
> >
> >
> > On Tue, 30 May 2023 at 17:08, Boro Sitnikovski  wrote:
> >>
> >> Hey,
> >>
> >> Thanks for the suggestion.
> >>
> >> For the previous case in the code, I added these in a Gist to not clutter 
> >> here too much:
> >>
> >> 1. The first example corresponds to 
> >> https://gist.github.com/bor0/b5f449bfe85440d96abd933b9f03b310#file-test_manual_group-php
> >> 2. The second example corresponds to 
> >> https://gist.github.com/bor0/b5f449bfe85440d96abd933b9f03b310#file-test_array_group-php
> >> 3. Another example, addressing the problem of increasing subsequences is 
> >> very simple with `array_group`: 
> >> https://gist.github.com/bor0/b5f449bfe85440d96abd933b9f03b310#file-test_array_incr_subseqs-php
> >>
> >> Best,
> >>
> >> Boro
> >>
> >>> On 30.5.2023, at 16:57, Andreas Hennings  wrote:
> >>>
> >>> Hello Boro,
> >>> I think you should include the "expected result" in your code examples.
> >>> Maybe this is in your patch file, but I don't think we want to look at
> >>> that for discussion.
> >>>
> >>> Cheers
> >>> Andreas
> >>>
> >>> On Tue, 30 May 2023 at 13:35, Boro Sitnikovski  
> >>> wrote:
> >>>>
> >>>> Hello all,
> >>>>
> >>>> As per the How To Create an RFC instructions, I am sending this e-mail 
> >>>> in order to get your feedback on my proposal.
> >>>>
> >>>> I propose introducing a function to PHP core named `array_group`. This 
> >>>> function takes an array and a function and returns an array that 
> >>>> contains arrays - groups of consecutive elements. This is very similar 
> >>>> to Haskell's `groupBy` function.
> >>>>
> >>>> For some background as to why - usually, when people want to do grouping 
> >>>> in PHP, they use hash maps, so something like:
> >>>>
> >>>> ```
> >>>>  >>>> $array = [
> >>>> [ 'id' => 1, 'value' => 'foo' ],
> >>>> [ 'id' => 1, 'value' => 'bar' ],
> >>>> [ 'id' => 2, 'value' => 'baz' ],
> >>>> ];
> >>>>
> >>>> $groups = [];
> >>>> foreach ( $array as $element ) {
> >>>>   $groups[ $element['id'] ][] = $element;
> >>>> }
> >>>>
> >>>> var_dump( $groups );
> >>>> ```
> >>>>
> >>>> This can now be achieved as follows (not preserving keys):
> >>>>
> >>>> ```
> >>>>  >>>> $array = [
> >>>> [ 'id' => 1, 'value' => 'foo' ],
> >>>> [ 'id' => 1, 'value' => 'bar' ],
> >>>> [ 'id' => 2, 'value' => 'baz' ],
> >>>> ];
> >>>>
> >>>> $groups = array_group( $array, function( $a, $b ) {
> >>>> return $a['id'] == $b['id'];
> >>>> } );
> >>>> ```
> >>>>
> >>>> The disadvantage of the first approach is that we are only limited to 
> >>>> using equality check, and we cannot group by, say, `<` or other 
> >>>> functions.
> >>>> Similarly, the advantage of the first approach is that the keys are 
> >>>> preserved, and elements needn't be consecutive.
> >>>>
> >>>> In any case, I think a utility function such as `array_group` will be 
> >>>> widely useful.
> >>>>
> >>>> Please find attached a patch with a proposed implementation. Curious 
> >>>> about your feedback.
> >>>>
> >>>> Best,
> >>>>
> >>>> Boro Sitnikovski
> >>>>
> >>
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] [Discussion] Add new function `array_group`

2023-05-30 Thread Andreas Hennings
Here we go,
https://3v4l.org/KsL3o


function array_group(array $arr1, callable $compare): array {
$groups = [];
$group = [];
$prev = NULL;
foreach ($arr1 as $value) {
if ($group && !$compare($prev, $value)) {
$groups[] = $group;
$group = [];
}
$group[] = $value;
$prev = $value;
}
if ($group) {
$groups[] = $group;
}
return $groups;
}

On Tue, 30 May 2023 at 18:21, Andreas Hennings  wrote:
>
> Thank you, this clarifies and it confirms my initial assumption of
> what you are proposing.
> So you want to slice an array by comparing adjacent values.
>
>
> My personal feedback:
> I think the need for the grouping behavior you describe is not common
> enough that it needs its own native function.
> I would say the more common desired behavior is the one in your first
> example. And even for that we don't have a native function.
>
> Your behavior can be implemented in userland like so:
> https://3v4l.org/epvHm
>
>
> $arr1 = array(1,2,2,3,1,2,0,4,5,2);
>
> $groups = [];
> $group = [];
> $prev = NULL;
> foreach ($arr1 as $value) {
> if ($group && $prev > $value) {
> $groups[] = $group;
> $group = [];
> }
> $group[] = $value;
> $prev = $value;
> }
> if ($group) {
> $groups[] = $group;
> }
>
> print_r($groups);
>
>
> If needed, the comparison function can be separated out and passed as
> a parameter.
> So the array_group() function with a comparison callback parameter can
> be implemented in userland.
>
> I think you need to make a case as to why the behavior you describe
> justifies a native function.
> E.g. if you find a lot of public php code that does this kind of grouping.
>
> I personally suspect it is not that common.
>
> Cheers
> Andreas
>
>
> On Tue, 30 May 2023 at 17:08, Boro Sitnikovski  wrote:
> >
> > Hey,
> >
> > Thanks for the suggestion.
> >
> > For the previous case in the code, I added these in a Gist to not clutter 
> > here too much:
> >
> > 1. The first example corresponds to 
> > https://gist.github.com/bor0/b5f449bfe85440d96abd933b9f03b310#file-test_manual_group-php
> > 2. The second example corresponds to 
> > https://gist.github.com/bor0/b5f449bfe85440d96abd933b9f03b310#file-test_array_group-php
> > 3. Another example, addressing the problem of increasing subsequences is 
> > very simple with `array_group`: 
> > https://gist.github.com/bor0/b5f449bfe85440d96abd933b9f03b310#file-test_array_incr_subseqs-php
> >
> > Best,
> >
> > Boro
> >
> > > On 30.5.2023, at 16:57, Andreas Hennings  wrote:
> > >
> > > Hello Boro,
> > > I think you should include the "expected result" in your code examples.
> > > Maybe this is in your patch file, but I don't think we want to look at
> > > that for discussion.
> > >
> > > Cheers
> > > Andreas
> > >
> > > On Tue, 30 May 2023 at 13:35, Boro Sitnikovski  
> > > wrote:
> > >>
> > >> Hello all,
> > >>
> > >> As per the How To Create an RFC instructions, I am sending this e-mail 
> > >> in order to get your feedback on my proposal.
> > >>
> > >> I propose introducing a function to PHP core named `array_group`. This 
> > >> function takes an array and a function and returns an array that 
> > >> contains arrays - groups of consecutive elements. This is very similar 
> > >> to Haskell's `groupBy` function.
> > >>
> > >> For some background as to why - usually, when people want to do grouping 
> > >> in PHP, they use hash maps, so something like:
> > >>
> > >> ```
> > >>  > >> $array = [
> > >> [ 'id' => 1, 'value' => 'foo' ],
> > >> [ 'id' => 1, 'value' => 'bar' ],
> > >> [ 'id' => 2, 'value' => 'baz' ],
> > >> ];
> > >>
> > >> $groups = [];
> > >> foreach ( $array as $element ) {
> > >>$groups[ $element['id'] ][] = $element;
> > >> }
> > >>
> > >> var_dump( $groups );
> > >> ```
> > >>
> > >> This can now be achieved as follows (not preserving keys):
> > >>
> > >> ```
> > >>  > >> $array = [
> > >> [ 'id' => 1, 'value' => 'foo' ],
> > >> [ 'id' => 1, 'value' => 'bar' ],
> > >> [ 'id' => 2, 'value' => 'baz' ],
> > >> ];
> > >>
> > >> $groups = array_group( $array, function( $a, $b ) {
> > >> return $a['id'] == $b['id'];
> > >> } );
> > >> ```
> > >>
> > >> The disadvantage of the first approach is that we are only limited to 
> > >> using equality check, and we cannot group by, say, `<` or other 
> > >> functions.
> > >> Similarly, the advantage of the first approach is that the keys are 
> > >> preserved, and elements needn't be consecutive.
> > >>
> > >> In any case, I think a utility function such as `array_group` will be 
> > >> widely useful.
> > >>
> > >> Please find attached a patch with a proposed implementation. Curious 
> > >> about your feedback.
> > >>
> > >> Best,
> > >>
> > >> Boro Sitnikovski
> > >>
> >

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] [Discussion] Add new function `array_group`

2023-05-30 Thread Andreas Hennings
Thank you, this clarifies and it confirms my initial assumption of
what you are proposing.
So you want to slice an array by comparing adjacent values.


My personal feedback:
I think the need for the grouping behavior you describe is not common
enough that it needs its own native function.
I would say the more common desired behavior is the one in your first
example. And even for that we don't have a native function.

Your behavior can be implemented in userland like so:
https://3v4l.org/epvHm


$arr1 = array(1,2,2,3,1,2,0,4,5,2);

$groups = [];
$group = [];
$prev = NULL;
foreach ($arr1 as $value) {
if ($group && $prev > $value) {
$groups[] = $group;
$group = [];
}
$group[] = $value;
$prev = $value;
}
if ($group) {
$groups[] = $group;
}

print_r($groups);


If needed, the comparison function can be separated out and passed as
a parameter.
So the array_group() function with a comparison callback parameter can
be implemented in userland.

I think you need to make a case as to why the behavior you describe
justifies a native function.
E.g. if you find a lot of public php code that does this kind of grouping.

I personally suspect it is not that common.

Cheers
Andreas


On Tue, 30 May 2023 at 17:08, Boro Sitnikovski  wrote:
>
> Hey,
>
> Thanks for the suggestion.
>
> For the previous case in the code, I added these in a Gist to not clutter 
> here too much:
>
> 1. The first example corresponds to 
> https://gist.github.com/bor0/b5f449bfe85440d96abd933b9f03b310#file-test_manual_group-php
> 2. The second example corresponds to 
> https://gist.github.com/bor0/b5f449bfe85440d96abd933b9f03b310#file-test_array_group-php
> 3. Another example, addressing the problem of increasing subsequences is very 
> simple with `array_group`: 
> https://gist.github.com/bor0/b5f449bfe85440d96abd933b9f03b310#file-test_array_incr_subseqs-php
>
> Best,
>
> Boro
>
> > On 30.5.2023, at 16:57, Andreas Hennings  wrote:
> >
> > Hello Boro,
> > I think you should include the "expected result" in your code examples.
> > Maybe this is in your patch file, but I don't think we want to look at
> > that for discussion.
> >
> > Cheers
> > Andreas
> >
> > On Tue, 30 May 2023 at 13:35, Boro Sitnikovski  wrote:
> >>
> >> Hello all,
> >>
> >> As per the How To Create an RFC instructions, I am sending this e-mail in 
> >> order to get your feedback on my proposal.
> >>
> >> I propose introducing a function to PHP core named `array_group`. This 
> >> function takes an array and a function and returns an array that contains 
> >> arrays - groups of consecutive elements. This is very similar to Haskell's 
> >> `groupBy` function.
> >>
> >> For some background as to why - usually, when people want to do grouping 
> >> in PHP, they use hash maps, so something like:
> >>
> >> ```
> >>  >> $array = [
> >> [ 'id' => 1, 'value' => 'foo' ],
> >> [ 'id' => 1, 'value' => 'bar' ],
> >> [ 'id' => 2, 'value' => 'baz' ],
> >> ];
> >>
> >> $groups = [];
> >> foreach ( $array as $element ) {
> >>$groups[ $element['id'] ][] = $element;
> >> }
> >>
> >> var_dump( $groups );
> >> ```
> >>
> >> This can now be achieved as follows (not preserving keys):
> >>
> >> ```
> >>  >> $array = [
> >> [ 'id' => 1, 'value' => 'foo' ],
> >> [ 'id' => 1, 'value' => 'bar' ],
> >> [ 'id' => 2, 'value' => 'baz' ],
> >> ];
> >>
> >> $groups = array_group( $array, function( $a, $b ) {
> >> return $a['id'] == $b['id'];
> >> } );
> >> ```
> >>
> >> The disadvantage of the first approach is that we are only limited to 
> >> using equality check, and we cannot group by, say, `<` or other functions.
> >> Similarly, the advantage of the first approach is that the keys are 
> >> preserved, and elements needn't be consecutive.
> >>
> >> In any case, I think a utility function such as `array_group` will be 
> >> widely useful.
> >>
> >> Please find attached a patch with a proposed implementation. Curious about 
> >> your feedback.
> >>
> >> Best,
> >>
> >> Boro Sitnikovski
> >>
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] [Discussion] Add new function `array_group`

2023-05-30 Thread Andreas Hennings
Hello Boro,
I think you should include the "expected result" in your code examples.
Maybe this is in your patch file, but I don't think we want to look at
that for discussion.

Cheers
Andreas

On Tue, 30 May 2023 at 13:35, Boro Sitnikovski  wrote:
>
> Hello all,
>
> As per the How To Create an RFC instructions, I am sending this e-mail in 
> order to get your feedback on my proposal.
>
> I propose introducing a function to PHP core named `array_group`. This 
> function takes an array and a function and returns an array that contains 
> arrays - groups of consecutive elements. This is very similar to Haskell's 
> `groupBy` function.
>
> For some background as to why - usually, when people want to do grouping in 
> PHP, they use hash maps, so something like:
>
> ```
>  $array = [
> [ 'id' => 1, 'value' => 'foo' ],
> [ 'id' => 1, 'value' => 'bar' ],
> [ 'id' => 2, 'value' => 'baz' ],
> ];
>
> $groups = [];
> foreach ( $array as $element ) {
> $groups[ $element['id'] ][] = $element;
> }
>
> var_dump( $groups );
> ```
>
> This can now be achieved as follows (not preserving keys):
>
> ```
>  $array = [
> [ 'id' => 1, 'value' => 'foo' ],
> [ 'id' => 1, 'value' => 'bar' ],
> [ 'id' => 2, 'value' => 'baz' ],
> ];
>
> $groups = array_group( $array, function( $a, $b ) {
> return $a['id'] == $b['id'];
> } );
> ```
>
> The disadvantage of the first approach is that we are only limited to using 
> equality check, and we cannot group by, say, `<` or other functions.
> Similarly, the advantage of the first approach is that the keys are 
> preserved, and elements needn't be consecutive.
>
> In any case, I think a utility function such as `array_group` will be widely 
> useful.
>
> Please find attached a patch with a proposed implementation. Curious about 
> your feedback.
>
> Best,
>
> Boro Sitnikovski
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Declaration-aware attributes

2023-05-30 Thread Andreas Hennings
On Tue, 30 May 2023 at 15:14, Stephen Reay  wrote:
>
> (Resending to the list without all the history because qmail complained about 
> message size)
>
>
> >>
> >> Hi Andreas,
> >>
> >> I too have wondered (and I think asked in room11?) about such a concept. 
> >> >From memory the general response was “just do it in userland with a 
> >> wrapper” so its good to see someone else is interested in this being part 
> >> of the language.
> >>
> >> While I agree that it’s most useful if the `Reflector` instance is 
> >> available in the constructor, I’m not keen on the proposed magic 
> >> “skipping” of arguments as you suggest. It seems way too easy to confuse 
> >> someone (particularly if the attribute class itself has reason to be 
> >> instantiated directly in code)
> >
> > Good point! Almost made me change my mind completely. But I already
> > changed it back :)
> >
> > When instantiating in code, the "real" signature would have to be
> > used, and the reflector argument passed explicitly.
>
>
> That’s kind of my point: it’s not super intuitive why (or the specifics of 
> how) it’s being skipped when it’s an attribute, vs when it’s instantiated 
> from code. What if someone specifies an argument with the same name? If they 
> specify args without names, can they just use null for that? Etc.

I agree it could be confusing.
But for the named args, I think it is quite obvious:

#[Attribute]
class A {
  public readonly array $moreArgs;
  public function __construct(
public readonly string $x,
// Reflector parameter can be last required parameter, BUT
#[AttributeDeclaration] public readonly \ReflectionClass $class,
// Optional parameters have to be after the required reflector parameter.
public readonly ?string $y = NULL,
// Variadic parameter must be last.
string ...$moreArgs,
  ) {
$this->moreArgs = $moreArgs;
  }
}

#[A('x', 'y', 'z')]  // -> new A('x', $reflector, 'y', 'z')
#[A(x: 'x', y: 'y')]  // -> new A('x', $reflector, 'y')
#[A(x: 'x', class: new ReflectionClass('C'))]  // -> new A('x', new
ReflectionClass('C'))

We _could_ say that explicitly passing a value for the reflector in an
attribute declaration is forbidden.
Or we allow it, then an explicit value would simply overwrite the
implicit value.

If we use placeholder syntax, the above examples would look like this:

#[A('x', ?, 'y', 'z')]  // -> new A('x', $reflector, 'y', 'z')
#[A(x: 'x', class: ?, y: 'y')]  // -> new A('x', $reflector, 'y')
#[A(x: 'x', class: new ReflectionClass('C'))]  // -> new A('x', new
ReflectionClass('C'))


>
> > This would be
> > useful for unit tests that want to replicate the realistic behavior.
> > Also it guarantees that the code of the attribute class can really
> > count on this value to not be null, no matter how the class is
> > instantiated.
> >
> >
>
> I would expect that whether the Reflector object is required is simply a 
> matter of whether or not the parameter is nullable.
> If it’s not nullable, then yes, the explicit instantiation call will need to 
> supply it at the correct location. If it’s only required when created from 
> attribute usage, then it would accept null, and the constructor would have 
> appropriate logic to handle that.
>

Yes.
But I would expect the common practice to be to make it required,
because then the constructor code will be simpler.

>
>
> >>
> >> I think a better approach would be to suggest authors put the parameter at 
> >> the *end* of the parameter list, so that no ‘skipping' is required when 
> >> passing arguments without names (or put it where you like if you’re always 
> >> using named arguments)
> >
> > If I understand correctly, the proposal would technically not change,
> > we just add a recommendation.
>
> Technically, yes “my way” would work fine with the proposal you’ve suggested, 
> if I choose to always put the parameter marked by #[ReflectionContext] last.

As above, the problem with this would be optional and variadic
parameters, which have to come after a required reflector parameter.

>
> I’m just concerned about confusing usage if “insert this parameter anywhere” 
> is the ‘recommended’ (i.e. documented example) way to use this feature.
>
> Even with that concern, I still prefer this to most other solutions mentioned 
> so far, for the same reasons: they’re all some degree of magic.
>
> The only other solution I can think of that’s less “magic” and more explicit, 
> is (and I have no idea if this is even feasible technically) to introduce a 
> builtin trait for attribute classes to use, providing a protected method or 
> property that gives access to the Reflector (how the trait has access is not 
> really important, I assume it can be assigned to the object somehow before 
> the constructor is called). I guess this could also be an abstract class, but 
> a trait makes it much easier to adopt so that would be my preferred approach.
>
> So something like
>
> trait AttributeReflector {
> protected function getReflector(): 

Re: [PHP-DEV] Declaration-aware attributes

2023-05-30 Thread Andreas Hennings
On Tue, 30 May 2023 at 05:22, Stephen Reay  wrote:
>
>
>
> > On 30 May 2023, at 07:48, Andreas Hennings  wrote:
> >
> > Hello internals,
> > I am picking up an idea that was mentioned by Benjamin Eberlei in the past.
> > https://externals.io/message/110217#110395
> > (we probably had the idea independently, but Benjamin's is the first
> > post where I see it mentioned in the list)
> >
> > Quite often I found myself writing attribute classes that need to fill
> > some default values or do some validation based on the symbol the
> > attribute is attached to.
> > E.g. a parameter attribute might require a specific type on that
> > parameter, or it might fill a default value based on the parameter
> > name.
> >
> > Currently I see two ways to do this:
> > 1. Do the logic in the code that reads the attribute, instead of the
> > attribute class. This works ok for one-off attribute classes, but it
> > becomes quite unflexible with attribute interfaces, where 3rd parties
> > can provide their own attribute class implementations.
> > 2. Add additional methods to the attribute class that take the symbol
> > reflector as a parameter, like "setReflectionMethod()", or
> > "setReflectionClass()". Or the method in the attribute class that
> > returns the values can have a reflector as a parameter.
> >
> > Both of these are somewhat limited and unpleasant.
> >
> > I want to propose a new way to do this.
> > Get some feedback first, then maybe an RFC.
> >
> > The idea is to mark constructor parameters of the attribute class with
> > a special parameter attribute, to receive the reflector.
> > The other arguments are then shifted to skip the "special" parameter.
> >
> > #[Attribute]
> > class A {
> >  public function __construct(
> >public readonly string $x,
> >#[AttributeContextClass]
> >public readonly \ReflectionClass $class,
> >public readonly string $y,
> >  ) {}
> > }
> >
> > $a = (new ReflectionClass(C::class))->getAttributes()[0]->newInstance();
> > assert($a instanceof A);
> > assert($a->x === 'x');
> > assert($a->class->getName() === 'C');
> > assert($a->y === 'y');
> >
> > Note that for methods, we typically need to know the method reflector
> > _and_ the class reflector, because the method could be defined in a
> > base class.
> >
> > #[Attribute]
> > class AA {
> >  public function __construct(
> >#[AttributeContextClass]
> >public readonly \ReflectionClass $class,
> >#[AttributeContextMethod]
> >public readonly ReflectionMethod $method,
> >  ) {}
> > }
> >
> > class B {
> >  #[AA]
> >  public function f(): void {}
> > }
> >
> > class CC extends B {}
> >
> > $aa = (new ReflectionMethod(CC::class, 
> > 'f))->getAttributes()[0]->newInstance();
> > assert($a->class->getName() === 'CC');
> > assert($a->method->getName() === 'f');
> >
> > ---
> >
> > Notice that the original proposal by Benjamin would use an interface
> > and a setter method, ReflectorAwareAttribute::setReflector().
> >
> > I prefer to use constructor parameters, because I generally prefer if
> > a constructor creates a complete and immutable object.
> >
> > 
> >
> > Thoughts?
> >
> > -- Andreas
> >
> > --
> > PHP Internals - PHP Runtime Development Mailing List
> > To unsubscribe, visit: https://www.php.net/unsub.php
> >
>
> Hi Andreas,
>
> I too have wondered (and I think asked in room11?) about such a concept. 
> >From memory the general response was “just do it in userland with a wrapper” 
> so its good to see someone else is interested in this being part of the 
> language.
>
> While I agree that it’s most useful if the `Reflector` instance is available 
> in the constructor, I’m not keen on the proposed magic “skipping” of 
> arguments as you suggest. It seems way too easy to confuse someone 
> (particularly if the attribute class itself has reason to be instantiated 
> directly in code)

Good point! Almost made me change my mind completely. But I already
changed it back :)

When instantiating in code, the "real" signature would have to be
used, and the reflector argument passed explicitly. This would be
useful for unit tests that want to replicate the realistic behavior.
Also it guarantees that the code of the attribute class can really
count on this value to not be null, no matter how the class is
instantiated

[PHP-DEV] Let ReflectionMethod keep track of original class

2023-05-29 Thread Andreas Hennings
Hello list,
this proposal will be useful in combination with "Declaration-aware attributes"


Problem

Currently, ReflectionMethod is not aware of the original class, if the
method is declared in a parent class.
Methods that are called during a discovery algorithm that need to
process a method with its original class typically need two
parameters:

function processMethod(\ReflectionClass $class, \ReflectionMethod $method) {..}


Proposal

Let a ReflectionMethod object keep track of the original class.
Introduce a new method ReflectionMethod->getOriginalClass() to retrieve it.

class B {
  function f($x) {}
}
class C extends B {}

foreach ([
  // There are different ways to get a reflection method object, all
of them track the original class.
  new ReflectionMethod('C', 'f'),
  (new ReflectionClass('C'))->getMethod('f'),
  (new ReflectionMethod('C', 'f'))->getParameters()[0]->getDeclaringFunction(),
] as $rm) {
  // The following won't change:
  assert($rm->class === 'B');
  assert($rm->getDeclaringClass()->getName() === 'B');
  // New method:
  assert($rm->getOriginalClass()->getName() === 'C');


Alternatives
==

At first I thought we might introduce a new class like
"VirtualReflectionMethod" which behaves as if the method was declared
on the child class. But this is awkward.

I think the ->getOriginalClass() is much simpler.


--- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Re: Declaration-aware attributes

2023-05-29 Thread Andreas Hennings
I just notice a flaw in my thinking.

On Tue, 30 May 2023 at 02:48, Andreas Hennings  wrote:
>
> Note that for methods, we typically need to know the method reflector
> _and_ the class reflector, because the method could be defined in a
> base class.

This is true when doing a discovery using attributes.
However, PHP does not know the original class when we call (new
ReflectionClass('C'))->getMethod('f')->getAttributes().
So it cannot pass the original class into the attribute constructor.

Currently, only the userland code that does the discovery knows the
original class.

We would need a type of method reflector that keeps track of the original class.
This could well be its own separate RFC.
Once this is done, we no longer need to pass the class as a separate argument.

So to keep this RFC independent, we should only pass the reflector
object where ->getAttributes()[*]->newInstance() was called on.
Then if in a separate RFC we keep track of the original class in
ReflectionMethod, the same information will be available when the
method reflector is injected into an attributes.

This also means that the ReflectionAttribute object needs to keep a
reference of the original reflector.
And if it does that, we can as well provide a method to return it.

assert((new ReflectionClass('C'))->getAttributes()[0]->getReflector()->getName()
=== 'C');

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Declaration-aware attributes

2023-05-29 Thread Andreas Hennings
Thanks for the feedback!

On Tue, 30 May 2023 at 03:43, Dusk  wrote:
>
> On May 29, 2023, at 17:48, Andreas Hennings  wrote:
> > Quite often I found myself writing attribute classes that need to fill
> > some default values or do some validation based on the symbol the
> > attribute is attached to.
> > E.g. a parameter attribute might require a specific type on that
> > parameter, or it might fill a default value based on the parameter
> > name.
>
> +1. This is a substantial limitation in the attribute system.
>
> > Currently I see two ways to do this:
> > 1. Do the logic in the code that reads the attribute, instead of the
> > attribute class. This works ok for one-off attribute classes, but it
> > becomes quite unflexible with attribute interfaces, where 3rd parties
> > can provide their own attribute class implementations.
> > 2. Add additional methods to the attribute class that take the symbol
> > reflector as a parameter, like "setReflectionMethod()", or
> > "setReflectionClass()". Or the method in the attribute class that
> > returns the values can have a reflector as a parameter.
>
> I see a third way which introduces less "magic":

Actually, these two options are what is possible with the current
version of PHP, but it requires to write php code to solve these
problems each time.
The actual proposal in terms of "language design" solution was further
below in my email :)


>
> 3.a. Add a method to ReflectionAttribute which retrieves the target of the 
> attribute as an appropriate reflection object. (Sadly, the obvious name 
> "getTarget" is already taken; I'll call it "getReflectionTarget" for now.)
>
> 3.b. Add a static method to ReflectionAttribute which, when called within an 
> Attribute constructor which is being called by 
> ReflectionAttribute::newInstance(), returns the ReflectionAttribute object 
> which is being instantiated.
>
> These features could be used together to set a default property on an 
> attribute based on its target, e.g.
>
> #[Attribute(Attribute::TARGET_PROPERTY)]
> class PropertyAnnotation {
>   public string $name;
>
>   public function __construct(?string $name = null) {
> $this->name = $name ?? 
> ReflectionAttribute::underConstruction()->getReflectionTarget()->getName();
>   }
> }
>
> Another variant that comes to mind is:
>
> 3.b. Add a new flag to attributes which causes the ReflectionAttribute object 
> to be passed to the constructor as the first argument, e.g.
>
> #[Attribute(Attribute::TARGET_PROPERTY | Attribute::WHO_MADE_ME)]
> class PropertyAnnotation {
>   public string $name;
>
>   public function __construct(ReflectionAttribute $attr, ?string $name = 
> null) {
> $this->name = $name ?? $attr->getReflectionTarget()->getName();
>   }
> }
>
> This is a little messier because it can't be used "under the covers" by an 
> attribute base class, but it accomplishes the same goals.

This is close to the parameter attribute I proposed.
The benefit of the parameter attribute is that a developer can clearly
see which parameter will be skipped for regular arguments.

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Re: Declaration-aware attributes

2023-05-29 Thread Andreas Hennings
A big TBD would be the new attribute classes we need to mark these parameters.
Perhaps we could use just one attribute class, e.g.
"AttributeDeclaration", and use the parameter type to determine which
part of the declaration is expected.

Also, if an attribute is allowed on different symbol types, then these
parameters could be made nullable, or allow multiple types.
Of course some combinations would be ambiguous, e.g.
`\ReflectionClass|\ReflectionMethod`.

#[Attribute(Attribute::TARGET_METHOD, Attribute::TARGET_PROPERTY,
Attribute::TARGET_CLASS_CONSTANT)]
class A {
  public function __construct(
public readonly string $x,
#[AttributeDeclaration] public readonly \ReflectionClass $class,
#[AttributeDeclaration] public readonly ?\ReflectionMethod $method,
#[AttributeDeclaration] public readonly
?\ReflectionClassConstant|\ReflectionProperty $constantOrProperty,
public readonly string $y,
  ) {}
}

On Tue, 30 May 2023 at 02:48, Andreas Hennings  wrote:
>
> Hello internals,
> I am picking up an idea that was mentioned by Benjamin Eberlei in the past.
> https://externals.io/message/110217#110395
> (we probably had the idea independently, but Benjamin's is the first
> post where I see it mentioned in the list)
>
> Quite often I found myself writing attribute classes that need to fill
> some default values or do some validation based on the symbol the
> attribute is attached to.
> E.g. a parameter attribute might require a specific type on that
> parameter, or it might fill a default value based on the parameter
> name.
>
> Currently I see two ways to do this:
> 1. Do the logic in the code that reads the attribute, instead of the
> attribute class. This works ok for one-off attribute classes, but it
> becomes quite unflexible with attribute interfaces, where 3rd parties
> can provide their own attribute class implementations.
> 2. Add additional methods to the attribute class that take the symbol
> reflector as a parameter, like "setReflectionMethod()", or
> "setReflectionClass()". Or the method in the attribute class that
> returns the values can have a reflector as a parameter.
>
> Both of these are somewhat limited and unpleasant.
>
> I want to propose a new way to do this.
> Get some feedback first, then maybe an RFC.
>
> The idea is to mark constructor parameters of the attribute class with
> a special parameter attribute, to receive the reflector.
> The other arguments are then shifted to skip the "special" parameter.
>
> #[Attribute]
> class A {
>   public function __construct(
> public readonly string $x,
> #[AttributeContextClass]
> public readonly \ReflectionClass $class,
> public readonly string $y,
>   ) {}
> }
>
> $a = (new ReflectionClass(C::class))->getAttributes()[0]->newInstance();
> assert($a instanceof A);
> assert($a->x === 'x');
> assert($a->class->getName() === 'C');
> assert($a->y === 'y');
>
> Note that for methods, we typically need to know the method reflector
> _and_ the class reflector, because the method could be defined in a
> base class.
>
> #[Attribute]
> class AA {
>   public function __construct(
> #[AttributeContextClass]
> public readonly \ReflectionClass $class,
> #[AttributeContextMethod]
> public readonly ReflectionMethod $method,
>   ) {}
> }
>
> class B {
>   #[AA]
>   public function f(): void {}
> }
>
> class CC extends B {}
>
> $aa = (new ReflectionMethod(CC::class, 
> 'f))->getAttributes()[0]->newInstance();
> assert($a->class->getName() === 'CC');
> assert($a->method->getName() === 'f');
>
> ---
>
> Notice that the original proposal by Benjamin would use an interface
> and a setter method, ReflectorAwareAttribute::setReflector().
>
> I prefer to use constructor parameters, because I generally prefer if
> a constructor creates a complete and immutable object.
>
> 
>
> Thoughts?
>
> -- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Declaration-aware attributes

2023-05-29 Thread Andreas Hennings
Hello internals,
I am picking up an idea that was mentioned by Benjamin Eberlei in the past.
https://externals.io/message/110217#110395
(we probably had the idea independently, but Benjamin's is the first
post where I see it mentioned in the list)

Quite often I found myself writing attribute classes that need to fill
some default values or do some validation based on the symbol the
attribute is attached to.
E.g. a parameter attribute might require a specific type on that
parameter, or it might fill a default value based on the parameter
name.

Currently I see two ways to do this:
1. Do the logic in the code that reads the attribute, instead of the
attribute class. This works ok for one-off attribute classes, but it
becomes quite unflexible with attribute interfaces, where 3rd parties
can provide their own attribute class implementations.
2. Add additional methods to the attribute class that take the symbol
reflector as a parameter, like "setReflectionMethod()", or
"setReflectionClass()". Or the method in the attribute class that
returns the values can have a reflector as a parameter.

Both of these are somewhat limited and unpleasant.

I want to propose a new way to do this.
Get some feedback first, then maybe an RFC.

The idea is to mark constructor parameters of the attribute class with
a special parameter attribute, to receive the reflector.
The other arguments are then shifted to skip the "special" parameter.

#[Attribute]
class A {
  public function __construct(
public readonly string $x,
#[AttributeContextClass]
public readonly \ReflectionClass $class,
public readonly string $y,
  ) {}
}

$a = (new ReflectionClass(C::class))->getAttributes()[0]->newInstance();
assert($a instanceof A);
assert($a->x === 'x');
assert($a->class->getName() === 'C');
assert($a->y === 'y');

Note that for methods, we typically need to know the method reflector
_and_ the class reflector, because the method could be defined in a
base class.

#[Attribute]
class AA {
  public function __construct(
#[AttributeContextClass]
public readonly \ReflectionClass $class,
#[AttributeContextMethod]
public readonly ReflectionMethod $method,
  ) {}
}

class B {
  #[AA]
  public function f(): void {}
}

class CC extends B {}

$aa = (new ReflectionMethod(CC::class, 'f))->getAttributes()[0]->newInstance();
assert($a->class->getName() === 'CC');
assert($a->method->getName() === 'f');

---

Notice that the original proposal by Benjamin would use an interface
and a setter method, ReflectorAwareAttribute::setReflector().

I prefer to use constructor parameters, because I generally prefer if
a constructor creates a complete and immutable object.



Thoughts?

-- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] [Discussion] Clone with

2023-04-19 Thread Andreas Hennings
On Tue, 18 Apr 2023 at 22:01, Bob Magic  wrote:
>
> > [1] In fact if the right hand side of with may be an expression that
> > evaluates to an array, folks wouldn't need to learn new syntax at all:
> >
> > $newProperties = [ "foo" => "bar" ];
> > clone $object with $newProperties;
> >
> > and
> >
> > clone $object with [ "foo" => "bar" ];
>
> in my opinion this is sick, as in awesome. the {} version makes no sense to
> me unless the language gains $var = {} ; to skip $var = (object)[];

I think the {...} is meant to be simply an "argument list", not a value.
But yes having an array here would add some flexibility. It would also
give us argument unpacking for free.

I wonder what this means for performance.
Will the expression always be evaluated as an array first, and then
applied, or can php do a "shortcut" where it internally treats it as
an argument list, even though the syntax implies array?
The different will be small, but it could become relevant if called
many times over.

-- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] [Discussion] Clone with

2023-04-18 Thread Andreas Hennings
> The biggest advantage of Nicolas' proposal over “clone with” is that it could 
> be made part of the interface contract whether a method modifies the object 
> state. So the PSR-7 ResponseInterface could look like the following:
> [..]
> public clone function withStatus($code, $reasonPhrase = '');

If this is the main argument, I think I would prefer a more general
"readonly" modifier on methods.

public readonly function withStatus($code, $reasonPhrase = ''): static;

> One cannot control whether $this should really be cloned: e.g. if a property 
> should only be modified based on certain conditions (e.g. validation), the 
> object would potentially be cloned in vain, resulting in a performance loss.

Exactly. I would say "conditionally clone", e.g. only clone if there
is a change, or if we don't already have a cached instance with this
value.

I think the "clone with" is much more flexible because we can call
this anywhere, not just in a dedicated method.

This said, I do agree with some of the benefits of the "Alternative" proposal.

-- Andreas


On Mon, 17 Apr 2023 at 08:32, Máté Kocsis  wrote:
>
> Hi Everyone,
>
> Quite some time after mentioning the "clone with" construct the first time
> (at the end of the
> https://wiki.php.net/rfc/write_once_properties#run-time_behaviour section),
> finally I managed to create a working implementation for this feature which
> would make it possible to properly modify readonly properties
> while simplifying how we write "wither" methods:
> https://wiki.php.net/rfc/clone_with
>
> Regards,
> Máté Kocsis

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] [Discussion] Clone with

2023-04-18 Thread Andreas Hennings
Hello Máté, internals,
I have been waiting for this to happen :)
Some feedback

> However, in some cases it would be useful to reference property names as 
> expressions, e.g. when one needs to use “clone with” in a foreach loop where 
> the index is the property name and the loop variable is the value to be 
> assigned. This is also possible using a slightly more verbose syntax:

What about argument unpacking?
I don't know if we can combine this with ":" syntax or only with "=>".

clone $object with {
  ...$arr,
  ...['a' => 'A', $b => 'B'],
  ...(function () {yield 'x' => 'X';})(),
  c: 'C',  // or
  d => 'D',  // No mixing of ':' and '=>', we have to choose one.
}

If we want to go crazy in the future:
(This would require another language feature of inline code blocks
with return value or behaving as generator)

clone $object with {
  ...{
yield 'a' => 'A';
yield 'b' => 'B';
  },
  ...{
return ['c' => 'C'],
  }
}

-- Andreas

On Mon, 17 Apr 2023 at 08:32, Máté Kocsis  wrote:
>
> Hi Everyone,
>
> Quite some time after mentioning the "clone with" construct the first time
> (at the end of the
> https://wiki.php.net/rfc/write_once_properties#run-time_behaviour section),
> finally I managed to create a working implementation for this feature which
> would make it possible to properly modify readonly properties
> while simplifying how we write "wither" methods:
> https://wiki.php.net/rfc/clone_with
>
> Regards,
> Máté Kocsis

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Array spread append

2023-04-05 Thread Andreas Hennings
> I have the feeling I'm missing something, but how would this compare to
array join

Array join does neither append nor replace if a numeric index already exists.
But you can do [...$arr0, ...$arr1].
https://3v4l.org/CIinR

print json_encode([
[...['a', 'b'], ...['c', 'd']],  // [a, b, c, d]
array_merge(['a', 'b'], ['c', 'd']),  // [a, b, c, d]
['a', 'b'] + ['c', 'd'],  // [a, b]
array_replace(['a', 'b'], ['c', 'd']),  // [c, d]
]);

(I wish we could use yaml for these outputs)


> $arr[...] = $arr2;

Using spread in the array index would be different from current usage
of the spread operator.
But it could be a nice alternative to `$arr = [...$arr, $arr2]`, as it
does not require to repeat the first variable.
So I am not against it.


--- Andreas

On Thu, 6 Apr 2023 at 00:36, Juliette Reinders Folmer
 wrote:
>
> On 6-4-2023 0:12, Vorisek, Michael wrote:
> > Hello,
> >
> > I would like to open a discussion for 
> > https://github.com/php/php-src/issues/10791 .
> > [https://opengraph.githubassets.com/a23cb565cc8acac6a33ecab5d9ee68a46f046a1ffe215501673156e506695430/php/php-src/issues/10791]
> > Array spread append · Issue #10791 · 
> > php/php-src
> > Description Currently spread operator can be used for almost anything. But 
> > not for array append. I propose the following to be supported:  > [1, 2]; $arr2 = [3, 4]; $arr[...] = $arr2; // ...
> > github.com
> > Appending N elements to an array is quite common language usage pattern and 
> > I belive it should be supported natively for shorter syntax, language 
> > consistency and performance.
> >
> > I am unable to implement it, but I am ready to help with RFC.
> >
> > Michael Vorisek
>
> I have the feeling I'm missing something, but how would this compare to
> array join (which is already available and is actually shorter than this) ?
>
> $arr += $arr2;
>
> Smile,
> Juliette
>
>

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] Saner array_(sum|product)()

2023-02-20 Thread Andreas Hennings
Hello,
the RFC seems like a good idea to me.
However, I do not see any mention of plus operator on arrays, e.g. ['a' =>
'A'] + ['b' => 'B']. The array_reduce() would support this, but I think
array_sum() would not, because of the return type. I just think this should
be made more explicit.
-- Andreas

On Mon, 13 Feb 2023, 02:48 G. P. B.,  wrote:

> Hello internals,
>
> If there are no further feedback I intend on opening the vote for this
> tomorrow.
>
> Best regards,
>
> George P. Banyard
>


Re: [PHP-DEV] Deprecated partially supported callables: should is_callable() throwa deprecation notice ?

2022-04-21 Thread Andreas Hennings
On Thu, 21 Apr 2022 at 11:18, Rowan Tommins  wrote:
>
> On Wed, 20 Apr 2022 at 19:16, Claude Pache  wrote:
>
> > > You make a very important claim in your bug report:
> > >
> > > > However, in real world, is_callable() is almost never given totally
> > arbitrary stuff
> > >
> > > My concern is that we have no way of knowing whether this is true, and
> > whether there will always be a clear action for users who see the
> > deprecation notice.
> >
> > In this case, there is always the possibility for them to use the error
> > suppression operator. That remains easy, and looks like a reasonable use
> > case of that operator.
> >
>
>
> The error suppression operator is a big blunt tool, and using it to squash
> a deprecation notice would be an absolute last resort. Note that in
> contrast to the strpos() case where both behaviours can be achieved with
> *permanent* changes, the error suppression would only be a temporary
> workaround for error logging, with no value in itself.
>
>
>
> > If we have to balance between addressing the verified issue of not
> > emitting deprecation notice for well-known and fairly frequent code
> > patterns, and addressing the potential and non-fatal issue of emitting too
> > much notices for totally unknown use cases, the choice should be
> > straightforward.
> >
>
>
> Again, this is true IF you are correct that one set of patterns is common
> and the other is not, but so far I've not seen any attempt to find out
> whether that's true. The idea of ignoring "unknown" use cases seems really
> dangerous - like closing your eyes so you can't see the bad effects of what
> you do. What we need to know is whether such use cases are *rare*, or
> whether the people using them are just not subscribed to this list.
>
> One starting point would be to find usages in the top 100 or 1000 packages
> on Packagist using https://github.com/nikic/popular-package-analysis -
> perhaps Juliette has already done that when testing their PHPCompatibility
> sniff?

As a package author, you might not really know where the value is coming from.
A parameter will be declared as `@param mixed $callback_or_not`, if it
internally calls the`is_callable()` method.
And a static analysis tool will only see `mixed`, not "mixed, but if
it is a string, it does not start with 'static::', or if i's an array,
the first value is not 'static'.".

On the other hand, the value probably won't be completely arbitrary.
It might be coming from a discovery process that reads yml files or
annotations, or that calls some kind of hook in php. Or it is
generated with string concatenation.
Whatever it is, it is probably explicitly or implicitly hard-coded somewhere.

Here is a starting point for a search. But only for explicit calls.
https://sourcegraph.com/search?q=context:global+is_callable%5C%28%28%5C%5B%27%28static%7Cself%29%27%7C%27%28static%7Cself%29::.*%27%29%2C+lang:php=regexp

>
> Regards,
> --
> Rowan Tommins
> [IMSoP]

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Deprecated partially supported callables: should is_callable() throwa deprecation notice ?

2022-04-19 Thread Andreas Hennings
A deprecation warning on is_callable() would imply that in a future
version of PHP that call will be illegal.
But this is not the case.
is_callable() is meant to accept any value, and return true or false.
is_callable('static::method') will still be a valid call in future
versions, only the result will be different.
This change simply cannot be covered with a deprecation warning.
Developers have to update their code when they upgrade to PHP 9.x, or
they need a PHP_VERSION_ID check, as sad as that is.

One solution could be to accept an additional parameter to enable the
forward behavior for is_callable().
But then this parameter would have to be removed again later.
Also, is_callable() already has two extra parameters, so it would get crowded.

-- Andreas

On Tue, 19 Apr 2022 at 17:18, Claude Pache  wrote:
>
>
>
> > Le 18 mars 2022 à 18:03, Juliette Reinders Folmer 
> >  a écrit :
> >
> > On 18-3-2022 14:37, Christoph M. Becker wrote:
> >> On 16.03.2022 at 06:52, Juliette Reinders Folmer wrote:
> >>> I've just been looking in detail at the Partially Supported Callables
> >>> deprecation RFC:
> >>> https://wiki.php.net/rfc/deprecate_partially_supported_callables
> >>> The RFC explicitly excludes the `is_callable()` function and the
> >>> `callable` type from throwing deprecation notices.
>  The |is_callable()| function and |callable| type remain side-effect
>  free and do not throw a deprecation warning. They will continue to
>  accept these callables until support is removed entirely.
> >>> While I can fully support this for the `callable` type, I wonder if the
> >>> decision to not throw a deprecation on use in `is_callable()` is the
> >>> right one (though I understand the desire to keep it side-effect free).
> >>> Consider these code samples:
> >>>   function foo(callable $callback) {}
> >>>   foo('static::method');
> >>> This function call not throwing a deprecation is not problematic as in
> >>> PHP 9.0 the function will start throwing a TypeError.
> >>>   if (is_callable('static::method')) {
> >>>   static::method();
> >>>   }
> >>> The second code sample, however, is problematic, as in PHP 9.0, the
> >>> behaviour of this code will be silently reversed for those callbacks
> >>> which would previously result in `is_callable()` returning true, which
> >>> makes this a potentially dangerous change without deprecation notice.
> >>> Would anyone care to enlighten me as to whether this was given due
> >>> consideration ?
> >> Frankly, I don't know.  Apparently, there was almost no discussion about
> >> that RFC.  Part of the reasoning to not raise E_DEPRECATED when calling
> >> is_callable() was likely the typical use case
> >>   $callable = …;
> >>   if (is_callable($callable)) {
> >>   call_user_func($callable);
> >>   }
> >> what would report the deprecation when actually calling the callable.
> >> Not sure what to do regarding your given use case(s).
> >> --
> >> Christoph M. Becker
> >
> > Unfortunately, those aren't the only places I see this happening and my 
> > example is not a stand-alone case either.
> >
> > I came across the above code sample ( is_callable('static::method') / 
> > is_callable(['parent', __FUNCTION__]) with variable method calls) in a 
> > number of different packages when testing a (PHPCompatibility) sniff I am 
> > writing to detect the deprecation, so this code pattern looks to be 
> > relatively common.
> >
> > Some examples:
> > * 
> > https://github.com/symfony/service-contracts/blob/bc0a2247c72d29241b5a06fb60dc1c9d9acf2a3a/ServiceSubscriberTrait.php#L39
> > * 
> > https://github.com/mockery/mockery/blob/c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac/library/Mockery/Mock.php#L960
> > * 
> > https://github.com/simplepie/simplepie/blob/dacf0ed495d2e8fb306e526ca3f2a846af78a7c9/tests/oldtests/absolutize/RFC3986.5.4/base.php#L13
> >
>
> As I think that it is a serious oversight of the RFC, I have open:
>
> https://github.com/php/php-src/issues/8401 
> 
>
> —Claude
>
>

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] Add true as type

2022-04-11 Thread Andreas Hennings
> sketch out a more complete picture for the type
system.  We can implement it in pieces and parts, but we should have a goal
in mind for where we want to land.

I think the type system in psalm is pretty cool. It does have fixed
literal values, but also deeply structured array types.
But:
- I don't think we'll arrive there in one leap
- I am not sure we _want_ to arrive there, ever. Perhaps it is just
too much for native support, especially if we want repeated runtime
checks.
- Runtime checks on array types seem like a nightmare.
- I am pretty confident that adding `true` won't close any doors fur
future type systems, if we already have `false`.

Alternatively we could look at compile-time type systems like in C++
(or Java?), where we don't have to waste time on runtime checks.
The default implicit variable type could be "variant", but variables
with a declared type would behave as in C++, with a faster and slimmer
storage model.
Of course any union type that allows values other than object or null
would have to use the storage model of "variant".
Sounds like a lot of work, but if we want to dream big, it is
something to consider.

(There might be better examples than C++, I only mention it because I
spent some time with it long ago. Perhaps even Java?)

-- Andreas

On Mon, 11 Apr 2022 at 18:18, Sara Golemon  wrote:
>
> On Mon, Apr 11, 2022 at 8:59 AM Tim Düsterhus  wrote:
> > For `true` the story is different, as this type is an entirely new type
> > available to userland and by the same argument one could have literal
> > strings or literal integers as distinct types as well. I wouldn't object
> > to that per se (and there's precedent in TypeScript), but there could be
> > a clearer plan for the type system as a whole, instead of adding types
> > piecemeal.
> >
>
> ^This. The bit about making a plan, I mean.  A slow erosion of what it
> means to be a type might be the easier pill to swallow, but it results in
> more chaos overall in the type system.  I'd like to see someone (literally
> anyone) take the time to sketch out a more complete picture for the type
> system.  We can implement it in pieces and parts, but we should have a goal
> in mind for where we want to land.
>
> Using this RFC as an example, it naturally leads into the question of value
> types (which is what `true` really is, a bool with a value of true), so why
> can't we say this method must return the number 42 or a string containing
> 'pamplemousse' ?  A roadmap would help us understand what those kinds of
> value types (in an unbounded type like string or a large bounded type like
> int) compared to the tight bounded type of bool.
>
> And yes.  Generics.   We can't talk about the type system without the
> subject of generics being extremely relevant.  Ignoring them because it's
> complicated is just kicking the can down the road.
>
> All that having been said, sure. I don't mind adding true for completeness,
> because it's fairly obvious and the scope of bool is fairly small, but I
> would really encourage anyone to step up and plan out a wider reaching goal
> for the type system.
>
> -Sara

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Interface for reflection entities who implements getAttributes method

2022-03-03 Thread Andreas Hennings
Hello list.
Another potential benefit of such interfaces can be polyfills.

PHP 8.x: class ReflectionClass implements Reflector, AttributesHaving {..}
PHP 7.x. class UserlandReflectionClass extends ReflectionClass
implements AttributesHaving {..}

function f(AttributesHaving $reflector) {
  $attributes = $reflector->getAttributes();
  ...
}

I wonder if it should be called "AttributeReflector" or
"AttributesHaving" or "AbleToHaveAttributesBeing" :)


On Fri, 25 Feb 2022 at 23:58, DANIEL VARGAS MUCCILLO
 wrote:
>
> Hi Internals, hope you are all well.
>
> I've been playing around with Attributes and found that the only
> way for safely type hint for reflection entities who implement the
> getAttributes method is the following union:
>
> ReflectionClass|ReflectionClassConstant|ReflectionFunctionAbstract|ReflectionParameter|ReflectionProperty
> > $reflectionEntity
>
>
> For a single use case this was ok, but i would like to survey if
> people here would be interested in the introduction of a interface
> against which we could correctly ensure, with less effort, that
> getAttribute is available.
>
> I believe that it would be something along the lines of:
>
> > interface AttributeReflector {
>
> public function getAttributes(?string $name = null, int $flags = 0):
> > array;
>
> }
>
>
> I have no experience with the PHP's source code, but if you think
> that it's a good idea I would like to propose an RFC along with a PoC
> patch to make this adition.
>
> Thanks for your time until now and all the good work I've seen here.
>
> --
> Daniel Vargas Muccillo

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Interface for reflection entities who implements getAttributes method

2022-03-03 Thread Andreas Hennings
On Tue, 1 Mar 2022 at 21:03, DANIEL VARGAS MUCCILLO
 wrote:
>
> >
> >  I *think* all Reflector children support attributes, so it may not need a
> > separate interface.
>
>
> ReflectionZendExtension and ReflectionExtension are currently the only ones
> who implement Reflector but don't support attributes.

There is also ReflectionAttribute itself which does not have getAttributes().

>
>
> > However, the entire Reflection class hierarchy is a mess and needs a
> > number of additional interfaces added to it generally.  It makes sense to
> > overhaul it holistically to make sure it all fits together.
> >
>
> Was not aware of other cases, but a quick look led to:
>
>- getExecutingFile() : string and getExecutingLine() : int in
>ReflectionFiber and ReflectionGenerator;
>- getDocComment() : string|false in ReflectionClass,
>ReflectionClassConstant, ReflectionFunctionAbastract and 
> ReflectionProperty;
>- getName(), getNameSpaceName() and getModifiers() in some cases (not
>always together).
>
> Should it be the case to expand the scope to handle these in the same
> proposal or maybe create a Meta RFC and discuss each on their own?
>
>
> > I have zero availability until mid-March, but I'm open to helping at that
> > point.
>
>
> Thanks for your return on that, I'll try to run at least a little on my own
> foot until there, so I can be less of a burden!
>
> --
> Daniel Vargas Muccillo

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] PHP Community to support Ukraine and help to stop Russian agression

2022-03-02 Thread Andreas Hennings
On Wed, 2 Mar 2022 at 18:07, Chase Peeler  wrote:
>
> On Wed, Mar 2, 2022 at 12:03 PM Alain D D Williams 
> wrote:
>
> > On Wed, Mar 02, 2022 at 05:43:25PM +0100, Victor Bolshov wrote:
> > > Just one more thing: as of yesterday, in response to western sanctions
> > and
> > > supplies of letal arms to Ukraine from EU countries, Putin has ordered
> > > Russian nuclear forces to switch to a special alert regime, which means
> > they
> > > will be ready to strike at any moment. Anything that can help stop this
> > > insanity, is welcome, IMO.
> >
> > How Putin will react to a banner on the PHP web site, or any other action,
> > is
> > anybody's guess.
> >
> >
> I'm pretty confident I can guess how he will react: he won't.

Putin would not be the direct target of such a statement.
The target would be anybody who visits php.net.
These would be mostly developers, but this is better than nothing.

Let's keep in mind there are php developers in Russia, in Ukraine, and
also in China.
Those in Ukraine currently have to fear for their lives, and they need
(at least) our solidarity.
Those in Russia (and China) have to suffer internet censorship, and
they risk to be locked away if they speak up, they need a way out of
the bubble.
Thirdly, whataboutists and bothsideists in the rest of the world need
a friendly reminder, or rather a kick in the butt.

I am happy for any contribution that works in this direction.
Even if we only reach web developers.
If regular media is being censored, then sites like php.net have to
pick up a part of that responsibility.

And no, just "war is bad" is not enough.
In fact at this point I consider it Russian propaganda.

And to anybody who is afraid of some disruption and debate in this mailing list:
Think of the disruption it would cause if your house gets bombed, the
freedom in your country is threatened, your family has to flee the
country.

-- Andreas Hennings

>
>
> > I have put something on my own web site. It is an expression of solidarity
> > with
> > Ukraine - something to encourage them. It Putin/Russia notices it I suspect
> > that it will only invite a DDOS attack or similar - if they do anything at
> > all.
> >
> > The big question: should there be something on the PHP web site. Hard to
> > answer
> > as the discussion has shown. I would say "no" but encourage everyone who
> > has a
> > personal site to do so and to raise discussion with their employer about so
> > doing.
> >
> > Maybe also help financially (eg URL below), but beware: scammers will have
> > set
> > up sites like this:
> >
> > https://www.donateukraine.com/
> >
> > Personally: I am in my late 60s and this is bringing back unpleasant
> > memories
> > of my 20s when we were seriously concerned about impending destruction from
> > nuclear war.
> >
> > Hugs to all in these difficult times.
> >
> > --
> > Alain Williams
> > Linux/GNU Consultant - Mail systems, Web sites, Networking, Programmer, IT
> > Lecturer.
> > +44 (0) 787 668 0256  https://www.phcomp.co.uk/
> > Parliament Hill Computers Ltd. Registration Information:
> > https://www.phcomp.co.uk/Contact.html
> > #include 
> >
> > --
> > PHP Internals - PHP Runtime Development Mailing List
> > To unsubscribe, visit: https://www.php.net/unsub.php
> >
> >
>
> --
> Chase Peeler
> chasepee...@gmail.com

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Re: Trait constructors / property initialization

2022-01-13 Thread Andreas Hennings
On Thu, 13 Jan 2022 at 15:36, Andreas Hennings  wrote:
>
> Hello list,
> I want to bring up a topic that has bothered me whenever I use traits.
> I don't have a solution proposal for it, yet, unfortunately.
> I was going to comment in the other thread about traits, but it seems
> better suited for a new discussion.
>
> --
>
> Traits allow to share code between classes of different inheritance chains.
> They can be used instead of composition, or they can be used to help
> with composition - e.g. a trait can expose functionality from a
> specific injected dependency.
>
> --
>
> When using base classes, we can follow a convention to always call the
> parent constructor.
> We can even make the properties in the base class private, to fully
> encapsulate them.
>
> But:
> When using properties in traits, how can I make sure that they are
> properly initialized in the class constructor?

I think I have a solution:
abstract properties in traits!

The idea would be:
- traits can have abstract properties that are private, protected or public.
- non-abstract classes cannot have abstract properties.
- class properties override trait properties, with some compatibility
requirements.
- non-abstract protected or public class properties from the parent
class also override trait properties.
- (optional) abstract classes can have abstract properties that are
protected or public.
- (optional) abstract class properties from a parent class are
overridden by the trait property, but with compatibility checks.

This implies that abstract trait properties _must_ be redeclared in
the class that uses the trait,

interface X {}

interface XHaving {
  public function getX(): X;
}

trait T {
  abstract private X $x;
  public function getX(): X {return $this->x;}
}

class C implements XHaving {
  use T;
  public function __construct(
private X $x,
  ) {}
}

class D implements XHaving {
  use T;  // Error, must redeclare abstract property T::$x.
}

The benefit:
Properties are initialized in the same file where they are declared.

I don't know if we need aliasing for properties, perhaps we should
first go without that.

I did find a discussion about abstract properties in externals.io, but
this was for interfaces and classes, not for traits.
https://externals.io/message/64126#66682

>
> Also, what if I want to provide an init method with specific logic to
> set that property? How can I make sure that method will be called in
> the constructor?

This part would not be solved by the abstract properties.
But I think that's ok.

>
> I found that Psalm can detect "PropertyNotSetInConstructor", which is
> also applied to properties from traits.
> But this is not as straightforward as calling a parent constructor.
>
> Can and should we provide a language-level solution for this?
> Or should this be left to static analysis tools and IDEs?
>
> Cheers,
> Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Trait constructors / property initialization

2022-01-13 Thread Andreas Hennings
Hello list,
I want to bring up a topic that has bothered me whenever I use traits.
I don't have a solution proposal for it, yet, unfortunately.
I was going to comment in the other thread about traits, but it seems
better suited for a new discussion.

--

Traits allow to share code between classes of different inheritance chains.
They can be used instead of composition, or they can be used to help
with composition - e.g. a trait can expose functionality from a
specific injected dependency.

--

When using base classes, we can follow a convention to always call the
parent constructor.
We can even make the properties in the base class private, to fully
encapsulate them.

But:
When using properties in traits, how can I make sure that they are
properly initialized in the class constructor?

Also, what if I want to provide an init method with specific logic to
set that property? How can I make sure that method will be called in
the constructor?

I found that Psalm can detect "PropertyNotSetInConstructor", which is
also applied to properties from traits.
But this is not as straightforward as calling a parent constructor.

Can and should we provide a language-level solution for this?
Or should this be left to static analysis tools and IDEs?

Cheers,
Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [VOTE] User Defined Operator Overloads

2022-01-05 Thread Andreas Hennings
On Wed, 5 Jan 2022 at 20:11, Jordan LeDoux  wrote:
>
>
>
> On Wed, Jan 5, 2022 at 6:33 AM Andreas Hennings  wrote:
>>
>> Hello Jordan,
>> do you have any thoughts about these symmetric/left/right modifiers,
>> to get rid of the $operandPos parameter?
>>
>> To me, the parameter could be the kind of thing where in hindsight we
>> ask "why?".
>>
>> Also, can we drop the "public" modifier, if we do a new version of the RFC?
>>
>> Cheers
>> Andreas
>
>
> It's a totally different design concept (symmetric/left/right) and I've been 
> going over the design implications before I responded. For instance, wouldn't 
> this be a special case of method overloading? You're overloading according to 
> a modifier, not the typing, but there is that to contend with. If someone 
> defined `symmetric operator +` and `left operator +` which would be used? (My 
> feeling is left as its more specific.)

You would not be allowed to provide both on the same class.

A class can provide either no operator, or the left operator, or the
right operator, or both.
A "symmetric" operator is a shortcut to providing both left and right,
but using the same implementation.
A class with a "symmetric" operator cannot also have a "left" or
"right" version of the same operator - this would be the same as two
methods with the same name.
However, if the parent class has a "symmetric" operator, you _can_
override the "left" and/or "right" version in the subclass. In that
case, if the subclass provides "left", then the parent "symmetric"
implementation would only be used for the "right" direction.

I am writing "right" and "left" here, but perhaps "normal" and
"inverted" would be less ambiguous.
Also, perhaps instead of "symmetric" we could write "left right".

Perhaps this code will clarify:

class C {
  symmetric operator * (int $other) {..}
  # left operator * (int $other) {..} // -> Error, already defined.
  left operator - (int $other) {..}
  right operator - (int $other) {..}
  left operator / (float $other) {..}
  left operator % (D $other) {..}
}

class D {
  right operator % (C $other) {..}
}

(new C()) * 5; // -> symmetric operator *
5 * (new C());  // -> symmetric operator *
(new C()) - 5;  // -> left operator -
5 - (new C());  // -> right operator -
(new C()) / 5;  // -> left operator /
5 / (new C());  // -> Error, no operator provided.

(new C()) % (new D());  // -> left operator % provided by C.
(new D()) % (new C());  // -> Error, not supported.

This means, the "right operator" will only ever be called if the left
side does not provide a "left operator".

Basically this is not so different from your RFC.
Just instead of having to do a "if ($operandPos ===
OperandPosition::LeftSide) {..}" inside an implementation, we are
providing two separate implementations.


> How would they be stored within the zend_class_entry? Since they would 
> technically have the same name, something would need to happen for them to 
> not be in the function table.

The right and left version would need to be distinguished somehow internally.
I would say "left" is the default, and "right" has a modifier to its name.
Perhaps for symmetric operators, we could store two distinct entries
internally, if that makes things easier.

>
> The public modifier is not required (as stated in the RFC), you can just add 
> it if you want. Are you asking for `public operator` to produce a compile 
> error?

That would be the idea.
Perhaps others will disagree.
Allowing the "public" introduces a meaningless choice that people can
argue about in their code style, and have pointless git commits where
they add or remove the modifier based on preference.
Of course the same could be said about "public" in interface methods.

-- Andreas

>
> Jordan

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [VOTE] User Defined Operator Overloads

2022-01-05 Thread Andreas Hennings
Hello Jordan,
do you have any thoughts about these symmetric/left/right modifiers,
to get rid of the $operandPos parameter?

To me, the parameter could be the kind of thing where in hindsight we
ask "why?".

Also, can we drop the "public" modifier, if we do a new version of the RFC?

Cheers
Andreas


On Tue, 4 Jan 2022 at 00:27, Andreas Hennings  wrote:
>
> Hello Jordan,
>
> I have another note about the RFC.
> (I am not sure what's the policy, if we should continue _all_
> discussion here or go back to the RFC thread. Hope it's ok here.)
>
> OperandPosition::LeftSide
> OperandPosition::RightSide
>
> I wonder if this is the best way to model this.
> Especially, it does not account for the case where an operator only
> works in one direction, or the allowed operand type is dependent on
> the direction.
> E.g., (Money / float) is ok, but (float / Money) probably not supported.
> Or if it is supported, then the return type will be quite different.
> You can throw an exception, but this is not useful for static analysis.
>
> An alternative syntax with a few more keywords:
>
> abstract class Money {
>   symmetric operator * (float|int $other): Money;  // Commutative.
>   left operator / (float|int $other): Money;  // Only $a / $b allowed,
> $b / $a not possible.
>   left operator - (Money $other): Money;  // $a - $b
>   right operator - (Money $other): Money;  // $b - $a
> }
>
> Btw, in the Matrix example from the RFC, under "When will $operandPos
> be useful?", the $operandPos is not really useful, because it will
> always pick the default $a - $b version.
> The same applies to "-" operator in my Money example, because $other
> already implements the operator.
>
> The $operandPos is only needed if the left operand does _not_
> implement the operator. Which is the case e.g. for complex numbers, or
> the Number class from the RFC example.
>
> I am trying to think of cases where ($a  $b) would have a
> different type than ($b  $a), but I can't think of any.
> Or rather, for any case that I could think of, the mirror operator
> could simply be provided by $b.
>
> I am not married to the modifier names, e.g. it could be "symmetric"
> or "commutative" or something else.
> For left/right perhaps I prefer to talk about the "default direction"
> vs the "flipped direction", not sure how this could be turned into
> keywords.
> If we don't like more keywords, perhaps something like "!operator" for
> the flipped version?
>
> Cheers
> Andreas
>
> On Mon, 3 Jan 2022 at 01:14, Jordan LeDoux  wrote:
> >
> > Hello internals,
> >
> > I've opened voting on
> > https://wiki.php.net/rfc/user_defined_operator_overloads. The voting will
> > close on 2022-01-17.
> >
> > To review past discussions on this RFC and the feature in general, please
> > refer to:
> >
> > - https://externals.io/message/116611 | Current RFC discussion
> > - https://externals.io/message/115764 | Initial RFC discussion
> > - https://externals.io/message/115648 | Pre-RFC discussion and fact-finding
> >
> > Jordan

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [VOTE] User Defined Operator Overloads

2022-01-03 Thread Andreas Hennings
On Tue, 4 Jan 2022 at 03:23, Jordan LeDoux  wrote:
>
>
>
> On Mon, Jan 3, 2022 at 4:05 PM Andreas Hennings  wrote:
>>
>> To me it is not surprising that an RFC like this would be controversial.
>> We could enter a wonderful new era of value objects with operators,
>> but it is hard to envision that beforehand.
>>
>> The best might be to find and advertise with use cases and examples
>> from other languages.
>> (as mentioned, I don't really know where to start looking, otherwise I
>> would have already shared something)
>
>
> Oh, this was always going to be a controversial RFC. :) Operator overloading 
> is controversial in most languages, even the ones that have it and don't 
> really have problems because of it. It's the kind of feature that doesn't 
> affect most people most of the time, but that all developers end up having a 
> strong opinion about one way or another. I talked about that a little bit in 
> the internals podcast episode I did with Derick.
>
> NumPy is probably the absolute best example of this feature being used. NumPy 
> is an absolute killer feature of Python, and drives an enormous amount of the 
> development being done in the language. It could easily be pointed out that 
> NumPy is the equivalent of an extension however, being compiled down and 
> directly hooking into the Python language. NumPy is also not included in the 
> core Python language, though operator overloading *is*, importantly. However, 
> the toolchain for adding NumPy is much easier and consistent than the 
> toolchain for PHP extensions in general, and NumPy didn't have to add 
> overloads themselves as part of the extension (or teach Python devs the rules 
> around using them in general).
>
> So as another example, I'll pull up the math libraries in PHP. I maintain a 
> math library myself, but it isn't nearly as widely used as the works by Mark 
> Baker. The mathematical uses have been described here as "niche" and "small". 
> First let's see what Mark has to say about operator overloading (and this RFC 
> specifically):
>
> > Likewise, I have libraries for Matrix and Complex Numbers, where this would 
> > be an incredibly useful addition a similar RFC using magic was proposed 
> > 6 years ago, and sadly never made it :-( I hope this time it is accepted, 
> > especially as a new keyword feels better than magic
> - Source: https://twitter.com/Mark_Baker/status/1477957056464359427
>
> The libraries that Mark is referring to, markbaker/matrix and 
> markbaker/complex, have over 105 million installs combined on packagist. 
> (Source: https://packagist.org/?query=markbaker )
>
> > And so would `$lineValue = $unitPrice * $quantity;` work fine, with less 
> > words to read... and people who don't understand operator precedence would 
> > have problems with more complex mathematical formulae whether it was 
> > operator overloading or method name
> - Source: https://twitter.com/Mark_Baker/status/1478137583989309440
>
> > But why shouldn't it be `==`... these are valid use cases for a comparison; 
> > equally, vector additions ($vector1 + $vector2) are quite commonplace in 
> > mathematics
> - Source: https://twitter.com/Mark_Baker/status/1478131480383606787
>
> Marco's response to the person who maintains the literal largest complex 
> mathematics library in the entire language was:
>
> > Yeah, so use matlab?
>
> This is what I'm struggling with. I am seeing a lot of things here that sound 
> an awful lot like "that's not *my* use case, so no". That isn't how you 
> design the language. Nikita's objection to the syntax is much easier for me 
> to understand, because it is based on *language design*.
>
> Mathematics isn't something that is never used or never needed in PHP. The 
> idea that I need to justify this statement is somewhat alarming.

I fully agree:
Any language should aim at doing math well, natively.
You should not have to want to go to another language for
calculations. If this is the case, the language is incomplete.
And I think modern PHP aims to be or become a "complete" language.

If people currently don't use the language for math problems, this
says more about the language than about the people.
There is a potential here to enable new packages that solve new
problems, that people would have done in another language in the past.

>
> I have provided not only a multitude of examples of use cases in the RFC, but 
> I have repeatedly done so on this list as well. I have provided examples in 
> mathematics and examples in other domains, such as with Collections, with 
> userland scalar objects. I've mentioned possible future scope to expand 
> further into things 

Re: [PHP-DEV] [VOTE] User Defined Operator Overloads

2022-01-03 Thread Andreas Hennings
On Mon, 3 Jan 2022 at 22:15, Jordan LeDoux  wrote:
>
> On Mon, Jan 3, 2022 at 9:07 AM Pierre  wrote:
>
> > I forgot to answer to that, specifically. I'd much more prefer to have
> > an explicit `equals(object $other): bool` (magic or not, really I do not
> > care) single method for equality only, that'd be great. In that sense, I
> > much preferred specific RFC about `__equalsTo()` or `__compareTo()`
> > alone that a huge generic operator overload RFC. I think both could
> > actually be separated, it wouldn't be that weird.
> >
>
> Based on the feedback so far, I am... concerned that doing so would leave
> the mathematical improvements out in the cold. It seems that a non-trivial
> number of voters do not see mathematics as a desirable use case on its own.
>
> There's not really anything preventing that part from being ripped out on
> its own, but the mathematical overloads add almost no complexity on their
> own to the patch. One of my main goals in contributing to PHP is to improve
> its usability and performance in mathematics.
>
> Using magic methods instead of the operator syntax would take perhaps 2
> hours to implement, it's a fairly trivial change in the scope of the RFC.
> However, I have no plans to bring this back in time for 8.2 should it be
> declined with a magic method implementation. Fundamentally, there are many
> voters that seem to be more concerned about possible abuse that empirically
> does not exist in most languages which have this feature, instead of
> improvements to one of the most neglected domains PHP is used in.
>
> And that's the real crux: *most* (but not all) of the objections raised so
> far suggest a future that factually does not exist in the example languages
> we can look at which already have this feature. C++ is perhaps the one real
> example of widespread abuse, however there are *multiple* aspects of this
> RFC which *specifically* target and discourage that kind of abuse.
> (Explicit typing of parameters, non-optional support for implied operators,
> restrictions on the return types of the == and <=> operators, errors and
> exceptions being propagated immediately, etc.)
>
> Further, the operand is not passed by-reference in this implementation,
> which flatly excludes many of the worst possible abuses.
>
> I understand all of these objections, but I do not agree with them.
> Obviously. If I did, then I wouldn't bring this RFC to a vote. What I have
> proposed is the most restricted version of operator overloads in any
> language I researched, and that is still not enough for many voters, some
> of whom have flatly stated that there is no version of this feature they
> would ever vote for. If that is the kind of headwind against quite
> fundamental improvements to mathematics within the language, then all of my
> energy will be required to produce any improvement at all, and I cannot
> spend effort on things which are unrelated.

To me it is not surprising that an RFC like this would be controversial.
We could enter a wonderful new era of value objects with operators,
but it is hard to envision that beforehand.

The best might be to find and advertise with use cases and examples
from other languages.
(as mentioned, I don't really know where to start looking, otherwise I
would have already shared something)

>
> Jordan

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [VOTE] User Defined Operator Overloads

2022-01-03 Thread Andreas Hennings
Hello Jordan,

I have another note about the RFC.
(I am not sure what's the policy, if we should continue _all_
discussion here or go back to the RFC thread. Hope it's ok here.)

OperandPosition::LeftSide
OperandPosition::RightSide

I wonder if this is the best way to model this.
Especially, it does not account for the case where an operator only
works in one direction, or the allowed operand type is dependent on
the direction.
E.g., (Money / float) is ok, but (float / Money) probably not supported.
Or if it is supported, then the return type will be quite different.
You can throw an exception, but this is not useful for static analysis.

An alternative syntax with a few more keywords:

abstract class Money {
  symmetric operator * (float|int $other): Money;  // Commutative.
  left operator / (float|int $other): Money;  // Only $a / $b allowed,
$b / $a not possible.
  left operator - (Money $other): Money;  // $a - $b
  right operator - (Money $other): Money;  // $b - $a
}

Btw, in the Matrix example from the RFC, under "When will $operandPos
be useful?", the $operandPos is not really useful, because it will
always pick the default $a - $b version.
The same applies to "-" operator in my Money example, because $other
already implements the operator.

The $operandPos is only needed if the left operand does _not_
implement the operator. Which is the case e.g. for complex numbers, or
the Number class from the RFC example.

I am trying to think of cases where ($a  $b) would have a
different type than ($b  $a), but I can't think of any.
Or rather, for any case that I could think of, the mirror operator
could simply be provided by $b.

I am not married to the modifier names, e.g. it could be "symmetric"
or "commutative" or something else.
For left/right perhaps I prefer to talk about the "default direction"
vs the "flipped direction", not sure how this could be turned into
keywords.
If we don't like more keywords, perhaps something like "!operator" for
the flipped version?

Cheers
Andreas

On Mon, 3 Jan 2022 at 01:14, Jordan LeDoux  wrote:
>
> Hello internals,
>
> I've opened voting on
> https://wiki.php.net/rfc/user_defined_operator_overloads. The voting will
> close on 2022-01-17.
>
> To review past discussions on this RFC and the feature in general, please
> refer to:
>
> - https://externals.io/message/116611 | Current RFC discussion
> - https://externals.io/message/115764 | Initial RFC discussion
> - https://externals.io/message/115648 | Pre-RFC discussion and fact-finding
>
> Jordan

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [VOTE] User Defined Operator Overloads

2022-01-03 Thread Andreas Hennings
On Mon, 3 Jan 2022 at 16:00, Marco Pivetta  wrote:
>
> Hey Andreas,
>
> On Mon, Jan 3, 2022 at 3:40 PM Andreas Hennings  wrote:
>>
>> I imagine that operator overloads can improve DX for these use cases,
>> but at a cost for the overall language.
>>
>> How would you (Marco) see the future of arithmetic libraries for time,
>> money etc without overloaded operators?
>> How would these calculations look like e.g. with infix functions?
>> Do you think this can eliminate the desire for operator overloads?
>>
>>
>> E.g. something like this?
>>
>> $ts0 = new Timestamp($seconds0);
>> $ts1 = new Timestamp($seconds1);
>> /** @var Duration $duration */
>> $duration = $ts1 - $ts0;  // Operator overload notation.
>> $duration = Duration::betweenTimestamps($ts0, $ts1);  // Static method 
>> notation.
>> $duration = $ts1->diff($ts0);  // Object method notation.
>> $duration = $ts0  $ts1  // Infix notation
>> based on static method.
>> $duration = $ts1  $ts0  // Infix notation based on object method.
>
>
> I'd probably use `$ts1->subtract($ts0)`, which doesn't seem to be that 
> unreadable, and there is no need to abbreviate it to "diff" either.
> Whether it needs to be static methods or instance methods depends on the 
> wished API design.

Ok for "subtract", I should have thought of that :)
Although for me "subtract" sounds like a possibly mutable method,
whereas "diff" sounds immutable.

This said, I am not really convinced that the current syntax is "good enough".

>
> What this RFC aims at is a mathematical language, inside another general 
> purpose language: for complex expressions, I'd probably run it in a subsystem 
> dedicated to this instead.

For isolated "complex expressions", perhaps.

But we should think about algorithms with multiple small math
operations, e.g. a foreach with a repeated calculations, some of them
in conditional branches, e.g. to calculate prices, taxes, rent etc. In
all these operations, we want static analysis to check compatibility
of operands.

I think these algorithms will become more readable with overloaded
operators, while any "embedded formula engine" would make them vastly
more complex.

Btw others already pointed out that we also want comparison of value
objects, not just math.
But I came up with the math use cases, so am sticking to it to be fair.

Personally I still don't have a clear opinion for or against, but I
also don't have voting rights, so...

>
> See for example:
>
>  * https://en.wikipedia.org/wiki/Expression_(mathematics) (I'm literally just 
> picking a complex example - don't even know how to read that properly)
>  * and its textual representation in 
> https://en.wikipedia.org/w/index.php?title=Expression_(mathematics)=edit=1
>
> ```php
> $expression = <<<'MATH'
> f(a)+\sum_{k=1}^n\left.\frac{1}{k!}\frac{d^k}{dt^k}\right|_{t=0}f(u(t)) + 
> \int_0^1 \frac{(1-t)^n }{n!} \frac{d^{n+1}}{dt^{n+1}} f(u(t))\, dt.
> MATH;
>
> $result = $userlandExpressionEngine->evaluate(
> $expression,
> [
>  // ... bind parameters here 
> ]
> );
> ```
>
> Heck, I can probably ask the `$userlandExpressionEngine` to render that 
> monstrosity for me (see attachment).
>
> Note that we do this stuff constantly for SQL, yet we haven't designed a 
> system for embedding SQL into the language,

Actually we do have query builders, to reduce the amount of literal
SQL strings in code, and to make it feel more "native".
And where we do have literal SQL, we have to be very careful not to
break things, especially when binding or escaping/encoding variables.

> and still, SQL is used many magnitudes more than all what was discussed in 
> this RFC.

This may be true currently, for the arithmetics use cases.
This said, the reason for a small amount of math libraries in PHP
(which I did not actually count) could be exactly because of a lack of
overloaded operators.
And I think timestamp calculations are already relevant enough in today's code.

It would be interesting to see how much overloaded operators are used
in Python, and whether the good outweighs the bad.
Unfortunately I don't really know where to start looking.

>
> Yes, strings are problematic to some degree, but it's still better than 
> increasing language complexity for a very edge case.
>
> Alternatively, a better way to embed other languages can be used, which would 
> be much more useful for things like Twig, Blade, PHPTal, SQL, etc: In 
> haskell, this is done via Template Haskell, which many like, and many loathe, 
> but is still useful for type-safe operations with a different language than 
> the "main" one: 
> htt

Re: [PHP-DEV] [VOTE] User Defined Operator Overloads

2022-01-03 Thread Andreas Hennings
One question that just occured to me:
Why allow the "public" keyword for visibility, if operators are public
automatically, and "private" and "protected" are not allowed?
Do we plan for private/protected operators in the future?
Shouldn't we eliminate meaningless choice?

On Mon, 3 Jan 2022 at 01:14, Jordan LeDoux  wrote:
>
> Hello internals,
>
> I've opened voting on
> https://wiki.php.net/rfc/user_defined_operator_overloads. The voting will
> close on 2022-01-17.
>
> To review past discussions on this RFC and the feature in general, please
> refer to:
>
> - https://externals.io/message/116611 | Current RFC discussion
> - https://externals.io/message/115764 | Initial RFC discussion
> - https://externals.io/message/115648 | Pre-RFC discussion and fact-finding
>
> Jordan

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [VOTE] User Defined Operator Overloads

2022-01-03 Thread Andreas Hennings
On Mon, 3 Jan 2022 at 02:07, Marco Pivetta  wrote:
>
> Hey Jordan,
>
> I've voted "no" on this one: infix functions may have been interesting, but
> adding a whole new type system around operators is really not worth it,
> given:
>
>  * Added AST nodes
>  * Added method definitions for niche use-cases
>  * Complexity in support for static analysis tools
>
> I personally don't see a reason to introduce all this for examples like the
> one with GMP, which was more readable before adopting userland custom
> operators.
>
> In addition to all that, we didn't even achieve custom operators anyway:
> it's just the built-in ones (this is why I mentioned infix functions), and
> the precedence, number and type of operands are fixed too (yes, it is a
> sensible starting choice, but very little "custom" about it).
>
> Overall, your RFC is exactly what I would expect a custom operator RFC for
> PHP to look like: I just don't think the feature is needed at all, as it
> only makes the language much more complex, for rare cases that I hope I
> will never ever have to debug in future.

Perhaps we need more discussion about use cases to justify this RFC?

I think the main use cases mentioned so far were for arithmetic
operations between value objects that represent money, time, or other
measurable values, or mathematical higher-order values (vectors etc).

I imagine that operator overloads can improve DX for these use cases,
but at a cost for the overall language.

How would you (Marco) see the future of arithmetic libraries for time,
money etc without overloaded operators?
How would these calculations look like e.g. with infix functions?
Do you think this can eliminate the desire for operator overloads?

E.g. something like this?

$ts0 = new Timestamp($seconds0);
$ts1 = new Timestamp($seconds1);
/** @var Duration $duration */
$duration = $ts1 - $ts0;  // Operator overload notation.
$duration = Duration::betweenTimestamps($ts0, $ts1);  // Static method notation.
$duration = $ts1->diff($ts0);  // Object method notation.
$duration = $ts0  $ts1  // Infix notation
based on static method.
$duration = $ts1  $ts0  // Infix notation based on object method.

I generally like names I can click on, if they don't get too long..
I like if the choice of implementation is knowable by just looking at the code.
(with a small range of runtime polymorphism)
I like if some functionality is implemented outside of object methods,
keeping interfaces and inheritance chains simple.

But for code with lots of calculations, a simple "-" feels simpler and
more explanatory than something like "diff" or "timediff", where I
have to reference and remember a specific method/function name.

-- Andreas


> Greets,
>
> Marco

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] RFC: Stop to automatically cast numeric-string to int when using them as array-key

2022-01-01 Thread Andreas Hennings
On Sun, 2 Jan 2022 at 06:20, Michael Morris  wrote:
>
> On Sat, Jan 1, 2022 at 10:47 PM Kirill Nesmeyanov  wrote:
>
> >
> > >Суббота, 1 января 2022, 17:41 +03:00 от Rowan Tommins <
> > rowan.coll...@gmail.com>:
> > >
> > >On 31/12/2021 00:21, Kirill Nesmeyanov wrote:
> > >> I support this behavior fix because in its current form, due to a
> > similar problem (almost?), all PSR-7 implementations contain bugs that
> > violate RFC7230 (section 3.2:
> > https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 ). Thus,
> > physically, by the standard, all headers can have the name "0" (like «0:
> > value»), but when stored inside implementations, it is converted to a
> > string and a problem arises ($message->getHeaders() //
> > returns array instead of array).


The solution is to cast the keys back to string when reading from the
array, IF the type matters.

foreach ($headers as $k => $values) {
  $name = (string) $k;
}

We could introduce an alternative to array_keys() that would do this
automatically, e.g. "array_keys_str()".


> > >
> > >You appear to be technically correct - the RFC defines a header name
> > >only as "token", which implies the following would all be valid HTTP
> > >headers:
> > >
> > >42: The Answer
> > >!: Bang
> > >^_^: Surprised
> > >
> > >In practice, it would be a bad idea to use any of these.
> > >
> > >Every single one of the field names registered with IANA [1] starts with
> > >a letter, and proceeds with only letters, digits, and hyphen ('-'). [The
> > >exception is "*", listed there as "reserved" to specifically prevent its
> > >use conflicting with the wild-card value in "Vary" lists.]
> > >
> > >I'm actually surprised this definition hasn't been updated with
> > >interoperability advice in recent revisions of the standard. I did find
> > >this general advice for internet message headers in RFC 3864 [2]:
> > >
> > > > Thus, for maximum flexibility, header field names SHOULD further be
> > > >  restricted to just letters, digits, hyphen ('-') and underscore ('_')
> > > >  characters, with the first character being a letter or underscore.
> > >
> > >The additional restriction on underscore ('_') in HTTP arises from CGI,
> > >which maps headers to environment variables. For instance, Apache httpd
> > >silently drops headers with anything other than letters, digits, and
> > >hyphen [3] to avoid security issues caused by environment manipulation.
> > >
> > >If I was developing a PSR-7 or similar library, I would be inclined to
> > >drop any header composed only of digits, and issue a diagnostic warning,
> > >so that it wouldn't escalate to a type error later. It certainly doesn't
> > >seem reasonable to change the entire language to work around that
> > >inconvenience.
> > >
> > >[1]  https://www.iana.org/assignments/http-fields/http-fields.xhtml
> > >[2]  https://datatracker.ietf.org/doc/html/rfc3864#section-4.1
> > >[3]  https://httpd.apache.org/docs/trunk/env.html#setting
> > >
> > >Regards,
> > >
> > >--
> > >Rowan Tommins
> > >[IMSoP]
> > >
> > >--
> > >PHP Internals - PHP Runtime Development Mailing List
> > >To unsubscribe, visit:  https://www.php.net/unsub.php
> >
> > I just gave an example of what at the moment can cause an exception in any
> > application that is based on the PSR. It is enough to send the header "0:
> > Farewell to the server". In some cases (for example, as is the case with
> > RoadRunner) - this can cause a physical stop and restart of the server.
> >
> > Just in case, I will repeat my thesis: I cannot imagine that anyone is
> > using this functionality consciously and that it is part of the real logic
> > of the application.

It is not really relevant weather this is used _consciously_.

>
>
> You don't have a lot of experience with legacy code then. PHP, particularly
> old PHP (like 4, 5.1 era) was used by a lot of idiots.
>
> I was one of those idiots (Perhaps I still am an idiot - jury is
> deliberating on that but I digress).

We don't need to assume incompetence.

Any code that deals with arrays _must_ consider this behavior, unless
the array keys are known to be only integers, or only non-integer-like
strings.

One obvious BC break:

What would be the value of $a in the following snippet?

$a = [];
$a['5] = 's';
$a[5] = 'n';
$a[7] = 'n';
$a['7'] = 's';

Currently it would be [5 => 'n', 7 => 's'].
With the "new" behavior, we'd have to decide what happens.
Can keys '5' and 5 coexist? ['5' => 's', 5 => 'n', 7 => 'n', '7' => 's']?
Or would assignment change the key type? [5 => 'n', '7' => 's']?
Or does the initial key type remain, and only the value changes? ['5'
=> 'n', 7 => 's']?

I would argue that the current behavior might still be the best we can
get for a general-purpose structure that can act as a vector or a map
or a mix of both.
The perceived awkwardness is just a result of trying to do everything at once.

Possible solutions:
- Dedicated array-reading methods that cast all keys to string on read.
- New structures, alternative to array, 

Re: [PHP-DEV] [RFC] User Defined Operator Overloads (v0.6)

2021-12-21 Thread Andreas Hennings
On Tue, 21 Dec 2021 at 23:20, Jordan LeDoux  wrote:
>
>
>
> On Tue, Dec 21, 2021 at 5:47 AM Andreas Hennings  wrote:
>>
>> I see the "Implied Operators" section.
>> I assume this means that a new instance will be created, and stored on
>> the same variable, _if_ the original operator is written in an
>> immutable way (which it should be)?
>>
>> E.g.
>>
>> $money = new Money(5);
>> $orig = $money;
>> $m2 = $money + new Money(2);
>> assert($money === $orig);  // Checking object identity.
>> assert($m2 !== $orig);
>> $money += new Money(1);  // Equivalent to $money = $money + new Money(1);
>> assert($money !== $orig);
>> assert($orig->amount() === 5);
>>
>> I think we need a strong recommendation to implement operators as immutable.
>
>
>  Yes. The documentation for operator overloads should be much larger than 
> this RFC, and if this passes my focus for the rest of 8.2 will be on two 
> things:
>
> - Working on a few smaller follow up RFCs (sorting/ordering enum, polymorphic 
> handler resolution)
> - Working to help on the documentation of this feature
>
> All of the examples in the documentation should be for immutable 
> implementations, and there should be an explicit recommendation for immutable 
> implementations as well. With operators, mutable versions are created with 
> the operators under the "Implied" section instead of by creating an immutable 
> implementation of the operator itself.

Right. But even for the "implied" operators, I would say "mutable"
should refer to the variable, but not to the object.
This is what I tried to communicate with the code example.

>
> Jordan

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] User Defined Operator Overloads (v0.6)

2021-12-21 Thread Andreas Hennings
On Tue, 21 Dec 2021 at 02:57, Jordan LeDoux  wrote:
>
>
>
> On Mon, Dec 20, 2021 at 4:43 PM Andreas Hennings  wrote:
>>
>> > The exact position of where that trade-off is 'worth it' is going to
>> > be different for different people. But one of the areas where PHP is
>> > 'losing ground' to Python is how Python is better at processing data
>> > with maths, and part of that is how even trivial things, such as
>> > complex numbers, are quite difficult to implement and/or use in
>> > userland PHP.
>>
>> Could be interesting to look for examples in Python.
>> I was not lucky so far, but there must be something..
>> ...
>> Btw it would be really interesting to find such a list of
>> recommendations for Python.
>> The reference you added,
>> https://isocpp.org/wiki/faq/operator-overloading#op-ov-rules, is for
>> C++, which is less comparable to PHP than Python is.
>
>
> During my research phase of this RFC I was able to review many different 
> takes on this from the Python space. Here is one example of a community 
> discussion about it:
>
> https://stackoverflow.com/questions/1552260/rules-of-thumb-for-when-to-use-operator-overloading-in-python
>
> One of the interesting things here is that *most* of the warnings for Python 
> users discussed at the link are actually *designed* to not be issues within 
> this RFC. That's on purpose of course, I tried to think about how some of the 
> design issues in Python could be improved.

Right.
Your RFC might be the best we can do in the current PHP world, and
better than what exists in some other languages.
The remaining concerns would apply to any operator overloading RFC in
current PHP, and would not be special to this one.

>
> In Python, the +, +=, and ++ operators are implemented independently. In this 
> RFC, you may only overload the + operator, and then the VM handles the 
> appropriate surrounding logic for the other operators. For instance, with 
> ++$obj or $obj++, you want to return either a reference or a copy, depending 
> on if it's a pre- or post-increment. In this RFC, the handling of when the 
> ZVAL is returned is handled by the VM automatically, and a subordinate call 
> to the opcode for + is made when appropriate. The reassignment += works 
> similarly, with the ZVAL's being assigned automatically and a subordinate 
> call. This vastly reduces the surface for inconsistency.

I see the "Implied Operators" section.
I assume this means that a new instance will be created, and stored on
the same variable, _if_ the original operator is written in an
immutable way (which it should be)?

E.g.

$money = new Money(5);
$orig = $money;
$m2 = $money + new Money(2);
assert($money === $orig);  // Checking object identity.
assert($m2 !== $orig);
$money += new Money(1);  // Equivalent to $money = $money + new Money(1);
assert($money !== $orig);
assert($orig->amount() === 5);

I think we need a strong recommendation to implement operators as immutable.

>
> Another warning that is discussed is around overloading the == operator. A 
> big reason for this is that the Python overloads do NOT require the == 
> overload to return a particular type. Because of this, overloading the == 
> operator can result in situations in Python where it is difficult to compare 
> objects for equality. However, in this RFC the == operator can only be 
> overloaded to return a boolean, so the semantic meaning of the operator 
> remains the same. Though you could of course do something terrible and mutate 
> the object during an equality comparison, you must return a boolean value, 
> ensuring that the operator cannot be co-opted for other purposes easily. 
> Additionally, the != and == cannot be independently implemented in this RFC, 
> but can in Python.
>
> In Python the inequality operators can be implemented independently: >, >=, 
> <=, <. They *also* are not required to return a boolean value. In this RFC, 
> independent overloads for the different comparisons are not provided. 
> Instead, you must implement the <=> operator and return an int. Further, the 
> int value you return is normalized to -1, 0, 1 within the engine. This 
> ensures that someone could not repurpose the > operator to pull something out 
> of a queue, for instance. (They could still repurpose >> to do so, but since 
> the shift left and shift right operators are not used in the context of 
> boolean algebra often in PHP, that's far less dangerous.) A future scope that 
> I plan on working on is actually having an Ordering enum that must be 
> returned by the <=> overload instead, that even more explicitly defines what 
> sorts of states can be returned from this overload.
>
> A lo

Re: [PHP-DEV] [RFC] User Defined Operator Overloads (v0.6)

2021-12-21 Thread Andreas Hennings
On Tue, 21 Dec 2021 at 10:13, Rowan Tommins  wrote:
>
> On 21/12/2021 00:43, Andreas Hennings wrote:
> > I think the example in the RFC is interesting, but not ideal to
> > advertise the RFC.
> > The example is with native scalar types and build-in operator 
> > implementations.
> > (I don't know how GMP works internally, but for an average user of PHP
> > it does not make sense to call this "overloaded")
>
>
> I think you have misunderstood the example. GMP doesn't work with scalar
> types, it works with its own objects; the general approach is to call
> gmp_init() with a string describing a large number that cannot be
> represented by a PHP integer. This gives you an object which doesn't
> have any methods (it replaced a resource in older versions), but can be
> used with the gmp_* functions, and with mathematical operators
> overloaded in the engine.

Wow, you are right. I should read more before I post.
Thank you Rowan!
Sorry everybody for the distraction.

>
> So the questions you posed are not hypothetical:

Indeed.
The "concern" already applies for those extension-provided operator overloads.

>
> > - Are those variables scalar values, or objects?
> > - Are the operators using the built-in implementation or some custom
> > overloaded implementation? (depends on the operand types)
> > - Are the return values or intermediate values scalars or objects?
>
> They are objects, using an overloaded implementation of the operators,
> and returning more objects.

Well the initial values could be scalar or GMP. As soon as we hit any
gmp_*() function, the return type is going to be GMP.
In the rewritten example using mostly operators, the gmp_invert() is
the only part that guarantees the return type to be GMP.
Without that gmp_invert(), the return value could as well be scalar,
if all initial variables are.

float|GMP * float|GMP = float|GMP
gmp_mul(float|GMP, float|GMP) = GMP

>
> The only difference is that right now, you can only overload operators
> in an extension, not in userland code.

So the same "concern" already applies here,
But it can be outweighed by the benefit.

>
> Regards,
>
> --
> Rowan Tommins
> [IMSoP]
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] User Defined Operator Overloads (v0.6)

2021-12-20 Thread Andreas Hennings
On Tue, 21 Dec 2021 at 00:03, Dan Ackroyd  wrote:
>
> On Fri, 17 Dec 2021 at 18:36, Stanislav Malyshev  wrote:
> >
> > When reading
> > this code: $foo * $bar - how do I know which of the ways you took and
> > where should I look for the code that is responsible for it? When I see
> > $foo->times($bar) it's clear who's in charge and where I find the code.
> > Terse code is nice but not at the expense of making it write-only.
>
> Well, there's only two places  to look with operator overloads, but
> yes you're right, using operator overloads for single operation is not
> a good example of how they make code easier to read. The more
> complicated example from the introduction to the RFC
> https://wiki.php.net/rfc/user_defined_operator_overloads#introduction
> shows how they make complex maths easier to read.

I think the example in the RFC is interesting, but not ideal to
advertise the RFC.
The example is with native scalar types and build-in operator implementations.
(I don't know how GMP works internally, but for an average user of PHP
it does not make sense to call this "overloaded")

In fact, if we add overloaded operators as in the RFC, the example
becomes less easy to read, because now we can no longer be sure by
just looking at the snippet:
- Are those variables scalar values, or objects?
- Are the operators using the built-in implementation or some custom
overloaded implementation? (depends on the operand types)
- Are the return values or intermediate values scalars or objects?

We need really good variable names, and/or other contextual
information, to answer those questions.

This said, I am sure we _can_ find good examples.
In this thread, people already mentioned Matrix/Vector, Money/Currency
and Time/Duration.
Others would be various numbers with physical measuring units.

>
> The exact position of where that trade-off is 'worth it' is going to
> be different for different people. But one of the areas where PHP is
> 'losing ground' to Python is how Python is better at processing data
> with maths, and part of that is how even trivial things, such as
> complex numbers, are quite difficult to implement and/or use in
> userland PHP.

Could be interesting to look for examples in Python.
I was not lucky so far, but there must be something..

>
> Stanislav Malyshev wrote:
> > And again, what's the intuitive
> > difference between operators +=+@-+ and ++--=!* ?
>
> That's not part of the RFC.
>
> There's enough trade-offs to discuss already; people don't need to
> imagine more that aren't part of what is being proposed.
>
> > I have encountered
> > toolkits where the authors think it's cute to define "+" to mean
> > something that has nothing to do with mathematical addition
>
> Rather than leaving everyone to make the same mistakes again, this RFC
> might be improved by having a list of stuff that it really shouldn't
> be used for. At least then anyone who violates those guidelines does
> so at their own risk. Having guidelines would also help junior devs
> point out to more senior devs that "you're trying to be clever and the
> whole team is going to regret this".
>
> I started a 'Guidelines for operator overloads' here
> (https://github.com/Danack/GuidelinesForOperatorOverloads/blob/main/guidelines.md)
> - if anyone has horrorible examples they'd like to add, PR's are
> welcome.

I think it is a good start.
I would avoid appealing to "common sense" or "logical sense" though,
this can mean different things to different people, and is also
somewhat tautological, like "do good things, avoid bad things".
More meaningful terms can be "familiar", "expectations",
"predictable", "non-ambiguous".
(I see this language is coming from the C++ document, but either way I
don't like it)

Possible alternative language snippets:

For designing operators:
- Replicate familiar notations from the subject domain, e.g. maths,
physics, commerce. (this has some overlap with the first point)
- Return the type and value that people expect based on their
expectations and mental models.
- Use identifiers (class names, method names, variable names) from the
same subject domain language that inspires the operators.
- Avoid ambiguity: If different people will have different
expectations for return type and value, introduce well-named methods
instead of overloaded operators.
- Completeness trade-off: Understand the full range of operators, and
type combinations for the same operator, that is common in the subject
domain. Then decide which of those should be supported with operator
overloads, and which should be supported with methods instead.
- Take inspiration from code examples outside of PHP.

For using operators:
Use descriptive variable names, method names, other identifiers, and
other hints (@var comments etc), so that the type and role of each
variable and value can be easily understood.
E.g. "$duration = $tStart - $tEnd;".


"If you provide constructive operators, they should not change their operands."

I think we should 

Re: [PHP-DEV] [RFC] User Defined Operator Overloads (v0.6)

2021-12-20 Thread Andreas Hennings
On Fri, 17 Dec 2021 at 00:25, Larry Garfield  wrote:
>
> On Thu, Dec 16, 2021, at 1:24 PM, Andreas Hennings wrote:
>
> > I see the distinction in overloading based on the object type on the
> > left, vs overloading based on parameter types.
> >
> > For a method call $a->f($b), the implementation of ->f() is chosen
> > based on the type of $a, but not $b.
> > For an operator call "$a + $b", with the system proposed here, again,
> > the implementation of "+" will be chosen based on the type of $a, but
> > not $b.
> > For native operator calls, the implementation is chosen based on the
> > types of $a and $b, but in general they are cast to the same type
> > before applying the operator.
> > For global function calls f($a, $b), the implementation is always the same.
> >
> > In a language with parameter-based overloading, the implementation can
> > be chosen based on the types of $a and $b.
> >
> > This brings me back to the "symmetry" concern.
> > In a call "$a->f($b)", it is very clear that the implementation is owned by 
> > $a.
> > However, in an operator expression "$a + $b", it looks as if both
> > sides are on equal footing, whereas in reality $a "owns" the
> > implementation.
> >
> > Add to this that due to the weak typing and implicit casting,
> > developers could be completely misled by looking at an operator
> > invocation, if a value (in our case just the left side) has an
> > unexpected type in some edge cases.
> > Especially if it is not clear whether the value is a scalar or an object.
> > With a named method call, at least it is constrained to classes that
> > implement a method with that name.
>
> The RFC covers all of this, and the way it works around it.  Absent method 
> overloading (which I don't expect any time soon, especially given how 
> vehemently Nikita is against it), it's likely the best we could do.
>
>
> > In a class Matrix, operator(Matrix $other): Matrix {} can be declared
> > to always return Matrix, and operator(float $factor): float {} can be
> > declared to always return float.
> > However, with a generic operator(mixed $other): Matrix|float {}, we
> > cannot natively declare when the return value will be Matrix or float.
> > (a tool like psalm could still do it)
>
> I... have no idea what you're talking about here.  The RFC as currently 
> written is not a "generic operator".  It's
>
> operator *(Matrix $other, bool $left): Matrix
>
> The implementer can type both $other and the return however they want.  That 
> could be Matrix in both cases, or it could be Matrix|float, or whatever.  
> That's... the same as every other return type we have now.

Basically the same as others have been saying in more recent comments.

In a class Matrix, you might want to implement three variations of the
* operator:
- Matrix * Matrix = Matrix.
- Matrix * float = Matrix.
- Matrix * Vector = Vector.
Same for other classes and operators:
- Money / float = Money
- Money / Money = float
- Distance * Distance = Area
- Distance * float = Distance

Without parameter-based overloading, this needs union return types, IF
we want to support all variations with operators:
- Matrix * (Matrix|float|Vector) = Matrix|Vector.
- Money / (Money|float) = float|Money
- Distance * (Distance|float) = Area|Distance

Which gives you a return type with some ambiguity.

With methods, you could have different method names with dedicated return types.
The naming can be awkward, so I am giving different possibilities here.
- Matrix->mulFloat(float) = Matrix->scale(float) = Matrix
- Matrix->mul(Matrix) = Matrix::product(Matrix, Matrix) = Matrix
- Matrix->mulVector(Vector) = Vector

To me, the best seems a method name that somehow predicts the return type.

Possible solutions for the developer who is writing a Matrix class and
who wants to use overloaded operators:
- Accept the ambiguity of the return type, and use tools like psalm to
be more precise.
- Only use the * operator for one or 2 of the 3 variations (those that
return Matrix), and introduce a regular function for the third:
  - Matrix * Matrix|float = Matrix
  - Matrix->mulVector(Vector) = Vector

This "concern" is not a complete blocker for the proposal.
For math-related use cases like the above, the natural expectation to
use operators can be so strong that we can live with some return type
ambiguity.

-- Andreas


>
> --Larry Garfield
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] User Defined Operator Overloads (v0.6)

2021-12-16 Thread Andreas Hennings
On Thu, 16 Dec 2021 at 19:20, Dan Ackroyd  wrote:
>
> On Thu, 16 Dec 2021 at 12:21, Andreas Hennings  wrote:
>
> > Methods and functions have searchable and clickable names. Operators don't.
> > The "searchable" applies to grep searches in code, but also google
>
> That's one of the reasons why I prefer a magic methods based approach.
>
> function __plus(...){}
>
> can be searched for...and for future scope, something like:
>
> function __union(...){}
>
> is more self-documenting (imo) than:
>
> operator  ∪(...){}
>

I don't mind using magic methods for this, compared to an operator keyword.
It is also what I found is happening in python.

However, this does not give us searchability in the calling place,
only where it is declared / implemented.


>
> > Lack of real parameter overloading
> > Unlike C++ (or C?), we don't have real method/function overloading
> > based on parameters.
>
> Java is probably a better comparison language than C.
>
> I have a note on the core problem that method overloading would face
> for PHP here: https://phpopendocs.com/rfc_codex/method_overloading
>
> But although they both involved the word 'overloading', operator
> overloading, and method overloading are really separate features.

I see the distinction in overloading based on the object type on the
left, vs overloading based on parameter types.

For a method call $a->f($b), the implementation of ->f() is chosen
based on the type of $a, but not $b.
For an operator call "$a + $b", with the system proposed here, again,
the implementation of "+" will be chosen based on the type of $a, but
not $b.
For native operator calls, the implementation is chosen based on the
types of $a and $b, but in general they are cast to the same type
before applying the operator.
For global function calls f($a, $b), the implementation is always the same.

In a language with parameter-based overloading, the implementation can
be chosen based on the types of $a and $b.

This brings me back to the "symmetry" concern.
In a call "$a->f($b)", it is very clear that the implementation is owned by $a.
However, in an operator expression "$a + $b", it looks as if both
sides are on equal footing, whereas in reality $a "owns" the
implementation.

Add to this that due to the weak typing and implicit casting,
developers could be completely misled by looking at an operator
invocation, if a value (in our case just the left side) has an
unexpected type in some edge cases.
Especially if it is not clear whether the value is a scalar or an object.
With a named method call, at least it is constrained to classes that
implement a method with that name.

>
> > we cannot have the conditional type hints.
>
> btw you can just say 'types'.
>
> Unlike some lesser languages, in PHP parameter types are enforced at
> run-time; they aren't hints. I believe all references to hints (in
> relation to types at least) have been removed from the PHP manual.

Ok, what I mean is return type declarations.
In a class Matrix, operator(Matrix $other): Matrix {} can be declared
to always return Matrix, and operator(float $factor): float {} can be
declared to always return float.
However, with a generic operator(mixed $other): Matrix|float {}, we
cannot natively declare when the return value will be Matrix or float.
(a tool like psalm could still do it)

But even for parameters, if I just say "type" it won't be clear if I
mean the declared type on the parameter, or the actual type of the
argument value.


>
> cheers
> Dan
> Ack

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] User Defined Operator Overloads (v0.6)

2021-12-16 Thread Andreas Hennings
Hello internals,
some concerns I have about operator overloading.
(I have seen and played with operator overloading long time ago in
C++, this is my background for these points.)



1. Searchable names.
Methods and functions have searchable and clickable names. Operators don't.
The "searchable" applies to grep searches in code, but also google
searches for documentation and support.
This adds to the concerns already raised by others, that we will see
arbitrary operators "just because we can".



2. Symmetry
Operators like "+" or "==" are often expected to be symmetric / commutative.
(for "*" I would not universally expect this, e.g. matrix
multiplication is not symmetric)
https://en.wikipedia.org/wiki/Commutative_property
https://en.wikipedia.org/wiki/Symmetric_function
Having one side of the operator "own" the implementation feels wrong,
and could lead to problems with inheritance down the line.

>From C++ I remember that at the time, there was a philosophy of
defining and implementing these kinds of operations outside of the
objects that hold the data.



3. Lack of real parameter overloading
Unlike C++ (or C?), we don't have real method/function overloading
based on parameters.
I also don't see it being added in this operator RFC (and I would
disagree with adding it here, if we don't add it for functions/methods
first).
We have to solve this with inheritance override, and with if ($arg
instanceof ...) in the implementation.
What this does not give us is conditional return types:

This is what I would do in a language with parameter-based overloading:

class Matrix {
  operator * (Matrix $other): Matrix {..}
  operator * (float $factor): Matrix {..}
  operator * (Vector $vector): Vector {..}
}

class Vector {
  operator * (Vector $vector): float {..}
  operator * (float $factor): Vector {..}
}

(Or if we also have templates/generics, we could put dimension
constraints on the types, so that we cannot multiply matrices where
dimensions mismatch.)

Without real parameter overloading, we have to use instanceof instead,
and we cannot have the conditional type hints.


-- Andreas



On Thu, 16 Dec 2021 at 09:24, Pierre  wrote:
>
> Le 16/12/2021 à 05:01, Jordan LeDoux a écrit :
> > This is not a use case I highlighted because it's one that would be
> > difficult to support with this RFC. But as you say, it could be a good
> > future expansion. In particular, putting a query builder object into core
> > with some more advanced overloads built in may be the best way to
> > accomplish this, particularly if it is built with the idea in mind that the
> > entities themselves may also have overloads.
> >
> > I can certainly add it to the future scope of this RFC however.
> >
> > --
> >
> > RE: The operator keyword and operator implementations being non-callable.
> >
> > This was a limitation that I purposely placed on the operator keyword, as I
> > didn't like the idea of allowing syntax of the style `$obj->{'+'}();` or
> > `$obj->$op();` and I wanted developers to clearly understand that they
> > shouldn't treat these as normal methods in the vast majority of
> > circumstances. However, it seems this is one of the largest sticking points
> > for those who would otherwise support the RFC. To that end, I'm considering
> > removing that restriction on the `operator` keyword. If I were to do that,
> > you'd no longer need to wrap the operator in a closure to call it, though
> > the parser would still have problems with `$obj->+(...);`
> >
> > I suppose my question then would be, is this an acceptable compromise on
> > the operator keyword? It removes one of the more annoying hurdles that
> > Danack mentioned and that others have pointed out, but retains much of the
> > benefits of the keyword that I expressed in my last email.
> >
> > Jordan
>
> Hello,
>
> I'm not an internals hacker nor someone who can vote, but here is my
> opinion about operator overloading: I don't like it. Nevertheless, if it
> has to be done, I'd like it to be less challenging for PHP newcomers or
> everyday developers.
>
> An operator is not much more than a function shortcut, gmp examples
> prove that point quite well. I don't see why it is necessary to create a
> new syntax. It seems in the discussion that the magic method ship has
> sailed, but I'd much prefer it.
>
> I don't see why a user wouldn't be able to call an operator
> method/function outside of the operator context. It has a signature:
> left operand and right operand are its parameters, and it has a return
> type. The engine internally will just call this function as userland
> code could do.
>
> I think that adding a new syntax for it, and allowing weird function
> names which are the operator symbols will probably create some mind fuck
> in people's mind when reading the code. I like things being simple, and
> I'd love operator overloads to be simple functions, no more no less, not
> "a new thing". The more PHP syntax grows the more complex it is to learn
> and read.

Re: [PHP-DEV] Unified ReflectionType methods

2021-10-03 Thread Andreas Hennings
Another challenge would be array types like array or C[], or
array{i: int, s: string}.
And template/generic types like \Iterator.

Following the normalization idea, we could add a method getArrayTypes().
But I am not sure how far we get with this, I think there will be a
wall somewhere.

On Sun, 3 Oct 2021 at 08:18, Andreas Hennings  wrote:
>
> On Sat, 2 Oct 2021 at 16:37, tyson andre  wrote:
> >
> > Hi Andreas,
> >
> > > Hello list,
> > > I would like to propose new methods for ReflectionType, that would
> > > allow treating ReflectionNamedType and ReflectionUnionType in a
> > > unified way.
> > > This would eliminate the need for if (.. instanceof) in many use cases.
> > >
> > > Some details can still be discussed, e.g. whether 'null' should be
> > > included in builtin type names, whether there should be a canonical
> > > ordering of type names, whether we should use class names as array
> > > keys, etc.
> > > ...
> > > What do you think?
> >
> > Relatedly, I also had different ideas lately about new methods for 
> > ReflectionType, though of a different form.
> >
> > 1. To simplify code that would check `instanceof` for all current and 
> > future types such as `never` and `mixed` and intersection types
> > `ReflectionType->allowsValue(mixed $value, bool $strict = true): bool`
> >
> >Maybe also `allowsClass(string $className, bool $strict = true): bool` 
> > to avoid needing to instantiate values (weak casting allows 
> > Stringable->string).
>
> Ok for me, why not.
> I would see it as a distinct PR, separate from my original proposal.
> The problem or limitation of these methods is that they don't reflect
> the full expressiveness of the type system.
> That is, the return values of these methods would not be sufficient to
> recreate an equivalent type.
>
> > 2. To simplify code generation, e.g. in mocking libraries for unit testing: 
> > `ReflectionType->toFullyQualifiedString(): string` (e.g. `\A|\B`) (may need 
> > to throw ReflectionType for types that can't be resolved, e.g. `parent` in 
> > reflection of traits, keep `static` as is)
> >
> > (The raw output of `__toString()` isn't prefixed with `\` (e.g. `A`) 
> > and can't be used in namespaces
>
> Again, if we iron out the details, it can be a good idea as a distinct
> standalone PR.
>
> >
> > The fact that both intersection and union types (and possibility of union 
> > types of full intersection types)
> > make it hard for me to believe that getBuiltinTypes and getBuiltinClasses 
> > would be used correctly when used.
>
> Just to mention it, my proposed names were getBuiltinNames() and
> getClassNames() to be super clear that the return value will be
> string[].
>
> For future intersection types, there could be additional methods
> getBuiltinIntersectionTypes() and getClassIntersectionTypes(), or it
> would be a single method getIntersectionTypes(). Not sure yet which
> combos make sense. E.g. array would combine two builtin
> types, Countable would combine a class-like type and a
> builtin type.
> Every type would be broken into a normal form, where the top-level is
> treated as a union type, and each item can either be a pure builtin
> type, a pure class type, or an intersection of pure types.
>
> int|string|(A&(B|(C))) === int | string | (A) | (A)
>
> If we always use this normal form, then the ReflectionIntersectionType
> only needs to have getBuiltinNames() and getClassNames(), not
> getUnionTypes().
>
> Not sure yet what to do with 'static' and '$this'.
>
>
> >
> > Thanks,
> > Tyson
> > --
> > PHP Internals - PHP Runtime Development Mailing List
> > To unsubscribe, visit: https://www.php.net/unsub.php
> >

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Unified ReflectionType methods

2021-10-03 Thread Andreas Hennings
On Sun, 3 Oct 2021 at 07:40, Sebastian Bergmann  wrote:
>
> Am 02.10.2021 um 16:37 schrieb tyson andre:
> > `ReflectionType->allowsValue(mixed $value, bool $strict = true): bool`
>
> Not having to implement and maintain that functionality in userland would
> be brilliant and much appreciated. Right now we have
> https://github.com/sebastianbergmann/type for this for use cases related
> to test doubles in PHPUnit.

Wow. this package looks powerful.
But it also looks like it would require a lot of if/else, switch, or
dynamic dispatch, when dealing with those types.
E.g. if ($type->isUnion()) ...

>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Unified ReflectionType methods

2021-10-03 Thread Andreas Hennings
On Sat, 2 Oct 2021 at 16:37, tyson andre  wrote:
>
> Hi Andreas,
>
> > Hello list,
> > I would like to propose new methods for ReflectionType, that would
> > allow treating ReflectionNamedType and ReflectionUnionType in a
> > unified way.
> > This would eliminate the need for if (.. instanceof) in many use cases.
> >
> > Some details can still be discussed, e.g. whether 'null' should be
> > included in builtin type names, whether there should be a canonical
> > ordering of type names, whether we should use class names as array
> > keys, etc.
> > ...
> > What do you think?
>
> Relatedly, I also had different ideas lately about new methods for 
> ReflectionType, though of a different form.
>
> 1. To simplify code that would check `instanceof` for all current and future 
> types such as `never` and `mixed` and intersection types
> `ReflectionType->allowsValue(mixed $value, bool $strict = true): bool`
>
>Maybe also `allowsClass(string $className, bool $strict = true): bool` to 
> avoid needing to instantiate values (weak casting allows Stringable->string).

Ok for me, why not.
I would see it as a distinct PR, separate from my original proposal.
The problem or limitation of these methods is that they don't reflect
the full expressiveness of the type system.
That is, the return values of these methods would not be sufficient to
recreate an equivalent type.

> 2. To simplify code generation, e.g. in mocking libraries for unit testing: 
> `ReflectionType->toFullyQualifiedString(): string` (e.g. `\A|\B`) (may need 
> to throw ReflectionType for types that can't be resolved, e.g. `parent` in 
> reflection of traits, keep `static` as is)
>
> (The raw output of `__toString()` isn't prefixed with `\` (e.g. `A`) 
> and can't be used in namespaces

Again, if we iron out the details, it can be a good idea as a distinct
standalone PR.

>
> The fact that both intersection and union types (and possibility of union 
> types of full intersection types)
> make it hard for me to believe that getBuiltinTypes and getBuiltinClasses 
> would be used correctly when used.

Just to mention it, my proposed names were getBuiltinNames() and
getClassNames() to be super clear that the return value will be
string[].

For future intersection types, there could be additional methods
getBuiltinIntersectionTypes() and getClassIntersectionTypes(), or it
would be a single method getIntersectionTypes(). Not sure yet which
combos make sense. E.g. array would combine two builtin
types, Countable would combine a class-like type and a
builtin type.
Every type would be broken into a normal form, where the top-level is
treated as a union type, and each item can either be a pure builtin
type, a pure class type, or an intersection of pure types.

int|string|(A&(B|(C))) === int | string | (A) | (A)

If we always use this normal form, then the ReflectionIntersectionType
only needs to have getBuiltinNames() and getClassNames(), not
getUnionTypes().

Not sure yet what to do with 'static' and '$this'.


>
> Thanks,
> Tyson
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Unified ReflectionType methods

2021-10-01 Thread Andreas Hennings
Hello list,
I would like to propose new methods for ReflectionType, that would
allow treating ReflectionNamedType and ReflectionUnionType in a
unified way.
This would eliminate the need for if (.. instanceof) in many use cases.

Some details can still be discussed, e.g. whether 'null' should be
included in builtin type names, whether there should be a canonical
ordering of type names, whether we should use class names as array
keys, etc.


abstract class ReflectionType {

  [..] // existing methods.

  /**
   * @return string[]
   */
  public function getClassNames(): array;

  /**
   * @return \ReflectionClass[]
   */
  public function getClasses(): array {
$classes = [];
foreach ($this->getClassNames() as $className) {
  $classes[] = new ReflectionClass($className);
}
return $classes;
  }

  /**
   * @return string[]
   */
  public function getBuiltinNames(): array;
}

class ReflectionNamedType extends ReflectionType {

  [..] // existing methods.

  public function getClassNames(): array {
return $this->isBuiltin() ? [] : $this->getName();
  }

  public function getBuiltinNames(): array {
$names = $this->isBuiltin() ? [] : $this->getName();
if ($this->allowsNull()) {
  $names[] = 'null';
}
return $names;
  }
}

class ReflectionUnionType extends ReflectionType {

  [..] // existing methods.

  public function getClassNames(): array {
$namess = [];
foreach ($this->getTypes() as $type) {
  $namess[] = $type->getClassNames();
}
return array_unique(array_merge(...$types));
  }

  public function getBuiltinNames(): array {
$namess = [];
foreach ($this->getTypes() as $type) {
  $namess[] = $type->getBuiltinNames();
}
return array_unique(array_merge(...$types));
  }
}


What do you think?

-- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Static (factory) methods in attributes and initializers

2021-09-27 Thread Andreas Hennings
Hello Levi,
I thought it was clear.
By static "factory" method I just mean any static method that returns an object.
Don't interpret too much into the term :)

So to clarify, here is an example:

class OtherAttribute {}

class MyAttribute {
  public function __construct(string $name) {..}

  #[Attribute]
  public static function create(string $name): self {
return new self($name);
  }

  #[Attribute]
  public static function createOther($x): OtherAttribute {
return new OtherAttribute($x);
  }
}

class SomeValue {
  public static function create() {return new self();}
}

#[MyAttribute::create('hello')]
#[MyAttribute::createOther(SomeValue::create())]
class C {}

// This is the other part, static method calls in regular initializers.
function f($x = SomeValue::create())


Things to observe:
- The classes MyAttribute and OtherAttribute are not marked as
attribute via #[Attribute] annotation. Only the static methods are.
- MyAttribute::create() and MyAttribute::createOther() are allowed to
return any object that they want.

On Mon, 27 Sept 2021 at 18:26, Levi Morrison via internals
 wrote:
>
> I'm not exactly sure what you are proposing. However, it's always
> worth noting other languages which have something in the feature
> space, and Dart has both named constructors and factory constructors.
> You should start with researching what they do, and what other
> languages may have the same as _language features_, and not as
> patterns e.g. Java has a factory pattern, but it's not part of the
> language (at least not that I last checked, I don't follow Java).
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Static (factory) methods in attributes and initializers

2021-09-27 Thread Andreas Hennings
Hello list,

currently, the default mode for attributes is to create a new class.
For general initializers, with
https://wiki.php.net/rfc/new_in_initializers we get the option to call
'new C()' for parameter default values, attribute arguments, etc.

Personally I find class construction to be limiting, I often like to
be able to use static factories instead.
This allows:
- Alternative "constructors" for the same class.
- A single constructor can conditionally instantiate different classes.
- Swap out the class being returned, without changing the factory name
and signature.

In fact, static factories for initializers were already mentioned in
"Future Scope" in https://wiki.php.net/rfc/new_in_initializers.
However this does not mention static factories for the main attribute object.

For general initializers this is quite straightforward.
For attributes, we could do this?

// Implicitly call new C():
#[C()]
# Call the static factory instead:
#[C::create()]

So the only difference here would be that in the "traditional" case we
omit the "new " part.

We probably want to guarantee that attributes are always objects.
We can only evaluate this when somebody calls ->newInstance(), because
before that we don't want to autoload the class with the factory. So
we could throw an exception if the return value is something other
than an object.
I was also considering to require an explicit return type hint on the
factory method, but again this can only be evaluated when somebody
calls ->newInstance(), so the benefit of that would be limited.

The #[Attribute] annotation would allow methods as target.

Reflection:

::getArguments() -> same as before.
::getName() -> returns "$class_qcn::$method_name".
::getTarget() -> same as before.
::isRepeated() -> This is poorly documented on php.net, but it seems
to just look for other attributes with the same ->getName(). So it
could do the same here.
::newInstance() -> calls the method. Throws an error if return value
is non-object.

we could add more methods like ReflectionAttribute::isClass() or
ReflectionAttribute::isMethod(), or a more generic ::getType(), but
these are not absolutely required.

We could also allow regular functions, but this would cause ambiguity
if a class and a function have the same name. Also, functions cannot
be autoloaded, so the benefit would be small. I'd rather stick to just
methods.

-

Side note: I think "attributes" is a really poor name for findability.
But this ship has sailed.

-

Cheers
Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] Never For Argument Types

2021-09-08 Thread Andreas Hennings
On Sat, 14 Aug 2021 at 15:05, Larry Garfield  wrote:
>
> On Sat, Aug 14, 2021, at 7:48 AM, G. P. B. wrote:
> > On Sat, 14 Aug 2021 at 10:55, Deleu  wrote:
> >
> > > Hi Jordan,
> > >
> > > Does it make sense to explain in the RFC the difference between never and
> > > mixed in this context? The RFC vaguely mentions that never can never be
> > > used directly, but if it's limited to abstract class and interfaces, isn't
> > > that already impossible to use directly? Or does it mean that the type
> > > would "be allowed" on any function, but because of its intrinsic behavior
> > > it would always fail if used in non-abstract methods?
> > >
> > > Another clarification I'd be interested is about dropping the type
> > > declaration entirely (e.g. https://3v4l.org/a4bfs), because of covariance
> > > (or contravariance, I never know), sub classes can completely drop type
> > > declaration entirely. Will never not allow this? Why? Why not? If it does
> > > allow this, does it really differ from not having any type declaration on
> > > the abstract function?
> > >
> > > My knowledge in this area is practically null so if I'm asking stupid
> > > questions that are easily explained by some blog post I'd he happy to read
> > > it.
> > >
> >
> > never and mixed are on opposite sides of the type hierarchy.
> > mixed is the top type, meaning it's the supertype of any other types, and
> > any other type is a subtype of mixed (excluding void which is not really a
> > "type" if you look at it, from my understanding, in a type theory way).
> > never on the other side is the bottom type, meaning it's the subtype of any
> > other type, and any other type is a supertype of never.
> > Finally a lack of type declaration is treated as mixed.
> >
> > Liskov's substitutions rules dictate that return types are co-variant i.e.
> > more specific, and argument types are contra-variant i.e. more general.
> > This is why any return type can be replaced by never and any argument type
> > can have it's type dropped/changed to mixed.
> > As such replacing never by mixed in an argument is totally possible as
> > mixed is a wider type than never.
> >
> > How I personally see never as an argument type is that you require a
> > mandatory argument but you leave the type constraint up to the
> > implementation.
> >
> > A recent example which bit us in php-src/the PHP documentation is the
> > interface of ArrayAccess, all of the $offset parameters have a mixed type.
> > Until recently the ArrayObject/ArrayIterator had incorrect stubs [1] by
> > indicating that the argument was of type int|string, which practically is
> > the case as SPL's ArrayAccess handler will throw a TypeError on different
> > types, however this is done manually within the call and not when the call
> > is made.
> > Ideally the $offset parameter would be of type never such that SPL, and
> > userland, can specify what type of offsets they accept, be that the usual
> > int|string, only int for list-like objects, only string for dictionary-like
> > objects, maybe even object|int|string to allow GMP objects for arbitrary
> > precision.
> > Whereas every implementer of ArrayAccess is forced to accept mixed for the
> > $offset and need to manually enforce the type.
> >
> > This is the "power" of the never type as an argument type.
> >
> > Best regards,
> >
> > George P. Banyard
> >
> > [1]
> > https://github.com/php/php-src/pull/7215/files#diff-e330df347cac9e68d2d07a06535c534cd8c2438a1af703cd4245b8ce91ec65afL9-R10
>
> So... if I am following correctly, the idea is to allow `never` to be used in 
> an interface/abstract method only, as a way to indicate "you must specify a 
> type here of some kind; I don't care what, even mixed, but you have to put 
> something".  Am I following?
>
> I'm not sure on the type theory of it, but it does feel like a hack at first 
> blush.

You don't have to specify a type.
When overriding the method in a child interface or abstract class, it
must have a parameter in the same place (and ideally with same name,
to support named argument calls).
The parameter in the overridden method may or may not have a type hint.
The parameter type hint can be "never", as in the parent, but then you
cannot call the method.

See also this older discussion, https://externals.io/message/100275#100300

Example:

interface Fruit {..}
interface Apple extends Fruit {..}
interface Banana extends Fruit {..}

interface AbstractFruitEater {
  function eat(EMPTY_TYPE $fruit);
}

interface BananaEater extends AbstractFoodEater {
  function eat(Banana $banana);
}

interface AppleEater extends AbstractFoodEater {
  function eat(Apple $apple);
}

// In an ideal world, UniversalFruitEater would extend every other
FruitEater type, but that's not really possible.
interface UniversalFruitEater extends AbstractFoodEater /* ,
BananaEater, AppleEater */ {
  function eat(Fruit $fruit);
}



Btw, I wonder what this means for optional parameters.
Currently, "null" is not allowed as a 

Re: [PHP-DEV] Alias stdClass to DynamicObject?

2021-09-08 Thread Andreas Hennings
On Wed, 8 Sept 2021 at 23:00, Rowan Tommins  wrote:
>
> On 08/09/2021 16:37, Mike Schinkel wrote:
>
> > All future code that needs to refer to the class name will still refer to 
> > `stdClass`, so we won't be gaining much by creating an alias.
>
>
> Just to be clear, the only code that would need to change is code that
> dynamically gets *out* the class name, from get_class(), var_export(),
> reflection, and the like. Using the class name in code, like "$foo = new
> stdClass;" and "$foo instanceof stdClass", would carry on working just
> fine, whichever way we defined the alias.
>
>
> > Besides, don't forget `stdClass::class` in addition to `get_class()`.
>
>
> The ::class syntax is purely string replacement (apart from some rare
> edge cases), and works identically whether the class exists, is an
> alias, or doesn't exist at all.
>
>
> > I assume we would also disallow dynamic properties in anonymous classes 
> > too, right? After all, they are just statically declared classes that the 
> > developer do not assign a name.
>
>
> The difference I see is that stdClass/DynamicObject allows you to add or
> remove properties from an object *after it has been created*. I think a
> lot of use cases don't actually need that, and would benefit from error
> messages when doing so accidentally.
>
> You mentioned short-hand syntaxes like this:
>
> > $obj = {
> >  foo: 1,
> >  bar: "hello",
> >  baz: true,
> > };
>
> I would love for that, or some other short-hand, to be equivalent to this:
>
> $obj = new class(foo: 1, bar: "hello", baz: true) {
>  public $foo;
>  public $bar;
>  public $baz;
>  public function __construct($foo, $bar, $baz) {
> $this->foo  = $foo;
> $this->bar = $bar;
> $this->baz = $baz;
>  }
> }
>
> That is, an anonymous class, with exactly those three properties. If you
> *also* want to be able to define extra properties after it's created,
> you could opt into that using whatever mechanism a named class would
> (parent class, trait, attribute, etc; see other thread).
>
> Similarly, the objects created by json_decode or PDO_FETCH_OBJECT only
> need the *initial* properties to be dynamic, not to allow properties to
> be added later.

Even if no properties can be added after construction, this would
still mean that the list of properties can be determined at run-time.

There are two ways this can work:
1. Every new instance has its own anonymous class, even if they were
created by the same statement in code.
2. Different instances created by the same statement in code have the
same anonymous class, but this class supports dynamic properties.

Btw now that I think of it, I have seen lots of code where objects
from PDO are modified post construction, also with new properties
being added.


>
>
> Regards,
>
> --
> Rowan Tommins
> [IMSoP]
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Alias stdClass to DynamicObject?

2021-09-08 Thread Andreas Hennings
Wow!
I notice that ArrayObject already does everything we would need for
json_encode().

assert(json_encode(new ArrayObject([5])) === '{"0":5}');

However, it does so in a very strange way, not using any of the public
methods, but also not using dynamic properties.
It seems there is a hard-coded internal implementation when calling
json_encode() or converting to array.
https://3v4l.org/rAc4K

I think we would want something more clean and transparent.






On Wed, 8 Sept 2021 at 21:58, Andreas Hennings  wrote:
>
> On Wed, 8 Sept 2021 at 18:33, Lynn  wrote:
> >
> > On Wed, Sep 8, 2021 at 5:38 PM Mike Schinkel  wrote:
> >
> > > A couple more things; add a `JSON_OUTPUT_DYNAMIC_OBJECT` flag to output to
> > > DynamicObject for `json_decode()`,  add a 3rd parameter for flags to
> > > var_export() for the same reason, a `'return_dynamic_object'` option for
> > > `unserialize()`, and so on.
> > >
> >
> > It would also be interesting to enter a user-defined class here to work as
> > a reversed JsonSerializable. Could be a static factory method for all I
> > care, and would omit the requirement of serialization libraries for
> > "simple" things where you still want decent object typing. Could also work
> > together with interfaces accepting properties, effectively making the
> > result of json_decode an anonymous class that still adheres to an
> > interface. In case of the custom 'deserialization' you can throw exceptions
> > if the format is not correct. In the case of an anonymous class with an
> > interface it could throw an exception if the structure doesn't match
> > (possibly controlled by flags?).
>
> I think we want round-trips of json_decode() + json_encode() to work
> loss-free, without any custom classes or extra parameters.
>
> Currently, '{}' and '[]' have different results with json_decode().
>
> assert(json_encode(json_decode('{}')) === '{}');  // Perfect round-trip.
> assert(json_encode(json_decode('{}', JSON_OBJECT_AS_ARRAY)) === '[]');
>  // Lossy round-trip.
>
> This can only work if we have a built-in data transfer object,
> distinct from arrays.
> Currently this is \stdClass, using the dynamic property mechanism.
>
> But one could easily imagine a different kind of data transfer object,
> which would use a different mechanism instead of dynamic properties.
>
> A native implementation of JsonSerializable does not really work here,
> because ->jsonSerialize() would have to return \stdClass to result in
> '{}'.
> Instead, what about something with a "->getData(): array" method, but
> then json_decode() would encode it as '{}'?
>
> assert(json_decode('{}') instanceof DataTransferObject);
> assert(json_decode('{}')->getData() === []);
> assert(json_encode(json_decode('{}')->getData()) === '[]');
> assert(json_encode(json_decode('{}')) === '{}');
>
> For the proposed rename:
> - If we can fully deprecate and replace dynamic properties long-term,
> I would rather keep the name \stdClass until it dies.
> - Instead of an alias or rename, I would rather have a new data
> transfer object class which would not rely on dynamic properties, but
> on a new mechanism.
>
> Imo, creating an alias won't actually make life easier for anyone,
> unless we can fully remove and replace \stdClass.
> It would mean that developers need to learn both names, and understand
> how aliases work - something you don't really need to learn otherwise.
> Think of the silly wtfs that can occur if people don't fully
> understand aliases, or are not aware that DynamicObject is just an
> alias, while \ReflectionClass and get_class() still return 'stdClass'
> as the class name.
>
> -- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Alias stdClass to DynamicObject?

2021-09-08 Thread Andreas Hennings
On Wed, 8 Sept 2021 at 18:33, Lynn  wrote:
>
> On Wed, Sep 8, 2021 at 5:38 PM Mike Schinkel  wrote:
>
> > A couple more things; add a `JSON_OUTPUT_DYNAMIC_OBJECT` flag to output to
> > DynamicObject for `json_decode()`,  add a 3rd parameter for flags to
> > var_export() for the same reason, a `'return_dynamic_object'` option for
> > `unserialize()`, and so on.
> >
>
> It would also be interesting to enter a user-defined class here to work as
> a reversed JsonSerializable. Could be a static factory method for all I
> care, and would omit the requirement of serialization libraries for
> "simple" things where you still want decent object typing. Could also work
> together with interfaces accepting properties, effectively making the
> result of json_decode an anonymous class that still adheres to an
> interface. In case of the custom 'deserialization' you can throw exceptions
> if the format is not correct. In the case of an anonymous class with an
> interface it could throw an exception if the structure doesn't match
> (possibly controlled by flags?).

I think we want round-trips of json_decode() + json_encode() to work
loss-free, without any custom classes or extra parameters.

Currently, '{}' and '[]' have different results with json_decode().

assert(json_encode(json_decode('{}')) === '{}');  // Perfect round-trip.
assert(json_encode(json_decode('{}', JSON_OBJECT_AS_ARRAY)) === '[]');
 // Lossy round-trip.

This can only work if we have a built-in data transfer object,
distinct from arrays.
Currently this is \stdClass, using the dynamic property mechanism.

But one could easily imagine a different kind of data transfer object,
which would use a different mechanism instead of dynamic properties.

A native implementation of JsonSerializable does not really work here,
because ->jsonSerialize() would have to return \stdClass to result in
'{}'.
Instead, what about something with a "->getData(): array" method, but
then json_decode() would encode it as '{}'?

assert(json_decode('{}') instanceof DataTransferObject);
assert(json_decode('{}')->getData() === []);
assert(json_encode(json_decode('{}')->getData()) === '[]');
assert(json_encode(json_decode('{}')) === '{}');

For the proposed rename:
- If we can fully deprecate and replace dynamic properties long-term,
I would rather keep the name \stdClass until it dies.
- Instead of an alias or rename, I would rather have a new data
transfer object class which would not rely on dynamic properties, but
on a new mechanism.

Imo, creating an alias won't actually make life easier for anyone,
unless we can fully remove and replace \stdClass.
It would mean that developers need to learn both names, and understand
how aliases work - something you don't really need to learn otherwise.
Think of the silly wtfs that can occur if people don't fully
understand aliases, or are not aware that DynamicObject is just an
alias, while \ReflectionClass and get_class() still return 'stdClass'
as the class name.

-- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Option for array_column() to preserve keys.

2021-09-08 Thread Andreas Hennings
$source['a0']['b01'] = 5;On Wed, 8 Sept 2021 at 16:48, Andreas
Hennings  wrote:
>
> On Wed, 8 Sept 2021 at 16:10, Andreas Hennings  wrote:
> >
> > Thanks for the feedback so far!
> >
> > On Wed, 8 Sept 2021 at 10:13, Marco Pivetta  wrote:
> > >
> > > Heyo,
> > >
> > > On Wed, 8 Sep 2021, 02:19 Andreas Hennings,  wrote:
> > >>
> > >> Hello internals,
> > >>
> > >> The function array_column() would be much more useful if there was an
> > >> option to preserve the original array keys.
> > >> I can create an RFC, but I think it is better to first discuss the 
> > >> options.
> > >
> > >
> > > New function, please 
> >
> > I am not opposed. But I am also curious what others think.
> > What I don't like so much is how the situation with two different
> > functions will have a "historically grown wtf" smell about it.
> > But this is perhaps preferable to BC breaks or overly "magic"
> > parameters or overly crowded signatures.
> >
> > If we go for a new function:
> > A name could be array_column_assoc().
> >
> > array_column_assoc(array $array, string $value_key)
> >
> > This would behave the same as array_column($array, $value_key), but
> > preserve original keys.
> > Items which are not arrays or which lack the key will be omitted.
> > A $value_key === NULL would be useless, because this would simply
> > return the original array.
> >
> > The question is, should it do anything beyond the most obvious?
> > Or should we leave it minimal for now, with the potential for
> > additional parameters in the future?
> >
> > Limitations:
> > If some items are omitted, it will be awkward to restore the missing
> > items while preserving the order of the array.
> >
> > Possible ideas for additional functionality:
> > - Replicate a lot of the behavior of array_column(), e.g. with an
> > optional $index_key parameter. This would be mostly redundant.
> > - Additional functionality for nested arrays?
> > - Fallback value for entries that don't have the key? Or perhaps even
> > a fallback callback like with array_map()?
> > - Option to capture missing entries e.g. in a by-reference variable?
> >
> > A benefit of keeping the limited functionality would be that
> > programming errors are revealed more easily due to the strict
> > signature.
> >
> > A question is how we would look at this long term:
> > Do we want both functions to co-exist long-term, or do we want to
> > deprecate one of them at some point?
> > If array_column() is going to stay, then array_column_assoc() only
> > needs to cover the few use cases that are missing.
> >
> > -- Andreas
>
> If we want to support nested array structures, it could work like this:
>
> NOTE: We actually don't need to squeeze this into array_column_assoc().
> We could easily introduce a 3rd function instead, e.g.
> array_column_recursive(), if/when we want to have this in the future.
> I am only posting this so that we get an idea about the surrounding
> design space.
>
> $source['a']['b']['x']['c']['y'] = 5;
> $expected['a']['b']['c'] = 5;
> assert($expected === array_column_assoc($source, [null, null, 'x', null, 
> 'y']));
>
> Note the first NULL, which only exists to make the system feel more 
> "complete".
> This could be useful if the array is coming from a function call.
> The following examples show this:
>
> unset($source, $expected);  // (reset vars)
> $source['a']['x']['b'] = 5;
> $expected['a']['b'] = 5;
> assert($expected === array_column_assoc($source, [null, 'x']));
> assert($expected === array_column_assoc($source, 'x'));
>
> unset($source, $expected);  // (reset vars)
> $source['x']['a'] = 5;
> $expected['a'] = 5;
> assert($expected === array_column_assoc($source, ['x']));
> assert($expected === $source['x'] ?? []);
>
> Trailing NULLs do almost nothing, except to ensure that non-arrays are
> removed from the tree.
> I would have to think more about the details, but I think it would
> work like this:
>
> unset($source, $expected);  // (reset vars)
> $source['a0']['b'] = 5;
> $source['a1'] = 5;
> $expected = $actual;
> assert($expected === array_column_assoc($source, []));
> assert($expected === array_column_assoc($source, [null]));
> unset($expected['a1']);
> assert($expected === array_column_assoc($source, [null, null]));
> unset($expected['a0']);
> assert($expected === array_column_assoc($source, [null, null]));
>
> Another idea could be to "collapse

Re: [PHP-DEV] Option for array_column() to preserve keys.

2021-09-08 Thread Andreas Hennings
On Wed, 8 Sept 2021 at 16:10, Andreas Hennings  wrote:
>
> Thanks for the feedback so far!
>
> On Wed, 8 Sept 2021 at 10:13, Marco Pivetta  wrote:
> >
> > Heyo,
> >
> > On Wed, 8 Sep 2021, 02:19 Andreas Hennings,  wrote:
> >>
> >> Hello internals,
> >>
> >> The function array_column() would be much more useful if there was an
> >> option to preserve the original array keys.
> >> I can create an RFC, but I think it is better to first discuss the options.
> >
> >
> > New function, please 
>
> I am not opposed. But I am also curious what others think.
> What I don't like so much is how the situation with two different
> functions will have a "historically grown wtf" smell about it.
> But this is perhaps preferable to BC breaks or overly "magic"
> parameters or overly crowded signatures.
>
> If we go for a new function:
> A name could be array_column_assoc().
>
> array_column_assoc(array $array, string $value_key)
>
> This would behave the same as array_column($array, $value_key), but
> preserve original keys.
> Items which are not arrays or which lack the key will be omitted.
> A $value_key === NULL would be useless, because this would simply
> return the original array.
>
> The question is, should it do anything beyond the most obvious?
> Or should we leave it minimal for now, with the potential for
> additional parameters in the future?
>
> Limitations:
> If some items are omitted, it will be awkward to restore the missing
> items while preserving the order of the array.
>
> Possible ideas for additional functionality:
> - Replicate a lot of the behavior of array_column(), e.g. with an
> optional $index_key parameter. This would be mostly redundant.
> - Additional functionality for nested arrays?
> - Fallback value for entries that don't have the key? Or perhaps even
> a fallback callback like with array_map()?
> - Option to capture missing entries e.g. in a by-reference variable?
>
> A benefit of keeping the limited functionality would be that
> programming errors are revealed more easily due to the strict
> signature.
>
> A question is how we would look at this long term:
> Do we want both functions to co-exist long-term, or do we want to
> deprecate one of them at some point?
> If array_column() is going to stay, then array_column_assoc() only
> needs to cover the few use cases that are missing.
>
> -- Andreas

If we want to support nested array structures, it could work like this:

NOTE: We actually don't need to squeeze this into array_column_assoc().
We could easily introduce a 3rd function instead, e.g.
array_column_recursive(), if/when we want to have this in the future.
I am only posting this so that we get an idea about the surrounding
design space.

$source['a']['b']['x']['c']['y'] = 5;
$expected['a']['b']['c'] = 5;
assert($expected === array_column_assoc($source, [null, null, 'x', null, 'y']));

Note the first NULL, which only exists to make the system feel more "complete".
This could be useful if the array is coming from a function call.
The following examples show this:

unset($source, $expected);  // (reset vars)
$source['a']['x']['b'] = 5;
$expected['a']['b'] = 5;
assert($expected === array_column_assoc($source, [null, 'x']));
assert($expected === array_column_assoc($source, 'x'));

unset($source, $expected);  // (reset vars)
$source['x']['a'] = 5;
$expected['a'] = 5;
assert($expected === array_column_assoc($source, ['x']));
assert($expected === $source['x'] ?? []);

Trailing NULLs do almost nothing, except to ensure that non-arrays are
removed from the tree.
I would have to think more about the details, but I think it would
work like this:

unset($source, $expected);  // (reset vars)
$source['a0']['b'] = 5;
$source['a1'] = 5;
$expected = $actual;
assert($expected === array_column_assoc($source, []));
assert($expected === array_column_assoc($source, [null]));
unset($expected['a1']);
assert($expected === array_column_assoc($source, [null, null]));
unset($expected['a0']);
assert($expected === array_column_assoc($source, [null, null]));

Another idea could be to "collapse" array levels, using a magic value
other than NULL, that does not work as an array key.

unset($source, $expected);  // (reset vars)
$source['a0']['b0'] = 5;
$source['a1']['b1'] = 5;
$expected['b0'] = 5;
$expected['b1'] = 5;
assert($expected === array_column_assoc($source, [false]));
unset($expected);
$expected['a0'] = 5;
$expected['a1'] = 5;
assert($expected === array_column_assoc($source, [null, false]));

-- Andreas

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] Option for array_column() to preserve keys.

2021-09-08 Thread Andreas Hennings
Thanks for the feedback so far!

On Wed, 8 Sept 2021 at 10:13, Marco Pivetta  wrote:
>
> Heyo,
>
> On Wed, 8 Sep 2021, 02:19 Andreas Hennings,  wrote:
>>
>> Hello internals,
>>
>> The function array_column() would be much more useful if there was an
>> option to preserve the original array keys.
>> I can create an RFC, but I think it is better to first discuss the options.
>
>
> New function, please 

I am not opposed. But I am also curious what others think.
What I don't like so much is how the situation with two different
functions will have a "historically grown wtf" smell about it.
But this is perhaps preferable to BC breaks or overly "magic"
parameters or overly crowded signatures.

If we go for a new function:
A name could be array_column_assoc().

array_column_assoc(array $array, string $value_key)

This would behave the same as array_column($array, $value_key), but
preserve original keys.
Items which are not arrays or which lack the key will be omitted.
A $value_key === NULL would be useless, because this would simply
return the original array.

The question is, should it do anything beyond the most obvious?
Or should we leave it minimal for now, with the potential for
additional parameters in the future?

Limitations:
If some items are omitted, it will be awkward to restore the missing
items while preserving the order of the array.

Possible ideas for additional functionality:
- Replicate a lot of the behavior of array_column(), e.g. with an
optional $index_key parameter. This would be mostly redundant.
- Additional functionality for nested arrays?
- Fallback value for entries that don't have the key? Or perhaps even
a fallback callback like with array_map()?
- Option to capture missing entries e.g. in a by-reference variable?

A benefit of keeping the limited functionality would be that
programming errors are revealed more easily due to the strict
signature.

A question is how we would look at this long term:
Do we want both functions to co-exist long-term, or do we want to
deprecate one of them at some point?
If array_column() is going to stay, then array_column_assoc() only
needs to cover the few use cases that are missing.

-- Andreas

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Option for array_column() to preserve keys.

2021-09-07 Thread Andreas Hennings
Hello internals,

The function array_column() would be much more useful if there was an
option to preserve the original array keys.
I can create an RFC, but I think it is better to first discuss the options.

This is requested in different places on the web, e.g.
https://stackoverflow.com/questions/27204590/php-array-column-how-to-keep-the-keys/39298759

A workaround is proposed here and elsewhere, using array_keys() and
array_combine() to restore the keys.
However, this workaround not only adds complexity, but it breaks down
if some items don't have the value key. See https://3v4l.org/im2gZ.

A more robust workaround would be array_map(), but this is more
complex and probably slower than array_column(), for the given
purpose.

Some links for your convenience:
The function was introduced in this RFC, https://wiki.php.net/rfc/array_column
It is now documented here,
https://www.php.net/manual/en/function.array-column.php

Some ideas how this could be fixed:
1. Allow a magic value (e.g. TRUE) for the $index_key parameter, that
would cause the assoc behavior. To fully avoid BC break, this must be
a value that previously was completely forbidden. The value TRUE is
currently only forbidden with strict_types=1. A value of e.g. new
\stdClass is fully forbidden, but would be weird. A constant could be
introduced, but this would not prevent the BC concern.
2. Make the function preserve keys if $index_key === NULL. This would
be a full BC break.
3. Add an additional parameter with a boolean option or with integer
flags. This would be weird, because it would make the $index_key
parameter useless.
4. Add a new function.

Personally I would prefer option 1, with value TRUE (I can't think of
something better).

If I could change history, I would prefer option 2. The current
behavior could still be achieved with array_values(array_column(..)).

Regards,
Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] Partial function application

2021-05-13 Thread Andreas Hennings
On Sun, 25 Apr 2021 at 21:27, Larry Garfield  wrote:
>
> Greetings, Internalians!
>
> I would like to offer for your consideration another RFC, specifically syntax 
> for partial function application.
>
> https://wiki.php.net/rfc/partial_function_application
>
> It includes an implementation by Joe Watkins that is already about 95% 
> complete.  (There's some edge cases he's still sorting out, but all of the 
> typical cases should work already.)  Most of the design work comes from Levi 
> Morrison and Paul Crovella.  I helped out with the tests, a few edge bits, 
> and general instigator/nudge. :-)
>
> Discuss.

This is super nice! I can imagine many ways I would use it.

Some questions / thoughts.



1. Serializing:

Will the partial function be serializable?
I assume if the original function is anonymous, then neither the
original nor the partial will be serializable.
Also if any of the fixed parameter values is not serializable, the
partial function can't be either.

But outside of the above cases it should be technically possible, or not?



2. var_export()

Calling var_export() on a partial function could produce the original
code that generated the function.
E.g.

var_export(foo(?), TRUE) === 'foo(?)'

-

3. Parameter switching

Could we express the following as a partial function?

static function ($a, $b) {return foo($b, $a);}

E.g. as foo(?1, ?0) ?

Perhaps it doesn't need to be part of this RFC, but it is something to consider.



-- Andreas

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] noreturn type

2021-04-01 Thread Andreas Hennings
On Thu, 1 Apr 2021 at 15:05, Matthew Brown  wrote:
>
> On Tue, 30 Mar 2021 at 13:51, Ilija Tovilo  wrote:
>
> > Hi Matthew
> >
> > > I like the proposal. I also support the covariance.
> > >
> > > One question though.
> > > The RFC mentions possible future use of "nothing" as a bottom type for
> > > parameters in generics. This makes a lot of sense to me. Even without
> > > generics, it could be used for parameters in abstract methods.
> > >
> > > So why not already introduce "nothing" as the universal bottom type, and
> > > use it instead of "noreturn"?
> > > Can "noreturn" do anything that "nothing" can't?
> >
> > I'm also a little confused by this statement. The exact wording from the
> > RFC is:
> >
> > > Arguments for never: ... It's a full-fledged type, rather than a keyword
> > used in a specific situation. A far-in-the-future generics proposal could
> > use never as a placeholder inside contravariant generic types.
> >
> > From what I understand, in Hack noreturn and never are both
> > full-fledged types. Nothing is a bottom type while noreturn is not.
> > Since in your proposal noreturn would also be a bottom type there's no
> > reason why it couldn't be used in covariant or contravariant generic
> > parameters.
> >
> > Please correct me if I'm missing something.
> >
> > Ilija
> >
> > --
> > PHP Internals - PHP Runtime Development Mailing List
> > To unsubscribe, visit: https://www.php.net/unsub.php
> >
> >
> In Hack it's a bit confusing. I think the Hack team ultimately decided that
> "nothing" was a better name for the bottom type than "noreturn", and
> confusingly introduced one while the other was still active.
>
> They have a long-term plan to deprecate "noreturn" in favour of "nothing"
> and stop supporting the former name, though it's unclear when that might
> happen.

More reason for us to avoid this mistake, and introduce a name and
concept that takes into account "bottom types", or not?
We should decide now if we want to use the same keyword for
everything, or if a bottom type should be distinct from a no-return
behavior.

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [VOTE] noreturn type

2021-03-31 Thread Andreas Hennings
On Wed, 31 Mar 2021 at 14:41, Ilija Tovilo  wrote:
>
> Hi internals
>
> On Wed, Mar 31, 2021 at 2:14 PM Andreas Hennings  wrote:
> >
> > Also check my comment in the other thread:
> > If in the future we want a "bottom type" that also works for parameters
> > (generics or abstract methods), should we create a new keyword, or should
> > we attempt to find a keyword now that works for all cases?
> > Neither "never" nor "noreturn" seems suitable for use in parameters, but
> > "nothing" would be.
>
> While slightly misleading, noreturn absolutely could work as a
> parameter type, it's just not part of this RFC.

Technically any keyword would be "suitable" in parameters.
But having "noreturn" as a parameter type would look weird.

I think we should decide now if the new keyword should be suitable as
a parameter type name in the future or not.

> I don't see why nothing would be more suitable than never.

For me "nothing" looks a bit more like a type name than "never".
But seeing in your link that this already exists in typescript, I can
live with "never" :)

To your example below:
For me the real use case would be formal interfaces or base classes.
Ofc this will become more relevant with generics, but can already be
useful now with reflection-based architectures.

interface FormalHarvester {
  function harvest(never $x);
}

interface GraintHarvester extends FormalHarvester {
  function harvest(Grain $x);
}

So, if we introduce the "never" keyword now as a return type, with
this future use case in mind, that seems to be fine for me.

>
>
> I do think noreturn/never in parameter types could potentially be
> useful as it would serve as static analysis information and a runtime
> check at the same time.
>
> https://stackoverflow.com/a/39419171/1320374
>
> ```
> function shouldNotHappen(never $never): void {}
>
> /** @param 1|2|3 $foo */
> function foo($foo) {
> switch ($foo) {
> case 1: return 'One';
> case 2: return 'Two';
> }
>
> // Static analyzer can understand this value is not never but 3
> // Throws a type error at runtime when reached as nothing can satisfy 
> never
> shouldNotHappen($foo);
> }
> ```
>
> Although it's worth noting that match doesn't have that problem as it
> throws implicitly on unhandled values. So maybe we should focus our
> efforts on making match usable with code blocks instead of just
> expressions.
>
> Ilija
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



Re: [PHP-DEV] [VOTE] noreturn type

2021-03-31 Thread Andreas Hennings
Also check my comment in the other thread:
If in the future we want a "bottom type" that also works for parameters
(generics or abstract methods), should we create a new keyword, or should
we attempt to find a keyword now that works for all cases?
Neither "never" nor "noreturn" seems suitable for use in parameters, but
"nothing" would be.

-- Andreas

On Wed, 31 Mar 2021 at 05:35, Peter Stalman  wrote:

> Aw, my `terminus` suggestion didn't make it. ☹️
>
> Thanks,
> Peter
>


Re: [PHP-DEV] [RFC] noreturn type

2021-03-30 Thread Andreas Hennings
Hello,

I like the proposal. I also support the covariance.

One question though.
The RFC mentions possible future use of "nothing" as a bottom type for
parameters in generics. This makes a lot of sense to me. Even without
generics, it could be used for parameters in abstract methods.

So why not already introduce "nothing" as the universal bottom type, and
use it instead of "noreturn"?
Can "noreturn" do anything that "nothing" can't?

-- Andreas

On Tue, 30 Mar 2021 at 07:18, Matthew Brown 
wrote:

> > On Mar 29, 2021, at 1:25 PM, Ilija Tovilo 
> wrote:
> >
> > Hi Matthew
> >
> >> Ondřej Mirtes and I present an RFC for the noreturn type:
> >> https://wiki.php.net/rfc/noreturn_type
> >>
> >> The feature already exists in Hack (the primary inspiration) and is
> >> currently supported by our static analysis tools inside docblocks, and
> we
> >> feel there's a good argument for it to be supported by PHP itself.
> >
> > Thanks for the RFC! I'm very much in support of it.
> >
> > Two small things:
> >
> > 1. Some magic methods like __toString currently require a specific
> > return type (like string in that case). Since noreturn is a bottom
> > type technically it should be possible to type hint those magic
> > methods with noreturn. It's not a big issue if that's not possible,
> > but it should be mentioned in the RFC.
> >
> > 2. noreturn is one of the few return types that would technically make
> > sense for __construct (other than void).
> >
> > class Foo {
> >public function __construct(): noreturn {
> >throw new Exception();
> >}
> > }
> >
> > new Foo();
> > bar(); // < Dead code
> >
> > Not sure this is worth supporting but I just wanted to mention it.
> >
> > Ilija
> >
> > --
> > PHP Internals - PHP Runtime Development Mailing List
> > To unsubscribe, visit: https://www.php.net/unsub.php
> >
>
> Thanks, I’ll update the RFC to mention __toString, but I’ll steer clear of
> __construct
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>
>


Re: [PHP-DEV] Draft RFC: foreach iteration of keys without values

2020-09-15 Thread Andreas Hennings
On Wed, 16 Sep 2020 at 04:00, Josh Bruce  wrote:

>
> > * If PHP had either convention or special handling for _ or $_ as a
> “ignore this” destination, I wouldn’t have made the proposal.  However, it
> doesn’t; _ can (apparently!) be a constant, and is also a function, and $_
> has no special handling (and I bet it’s actually used to contain values
> that are used in at least one application).
>
> Saw this today and the list() each() made me think of this thread:
> https://www.dyn-web.com/php/arrays/iterate/
>
> With you on void for same reason and what if the double-arrow was all that
> was needed, like the empty + comma in the link:
>
> foreach ($arr as $key =>)
>

I like this!
Once you get over the perception that something is missing, it is
quite obvious what is going on.

Otherwise, I prefer "foreach ($arr as $key => void)" over "foreach ($arr as
$key => null)".
With "=> null" one might think this is a fancy alternative to
array_filter(). "void" is more explicitly "not a value".

The array_keys() does not work for iterators.

And even on arrays it can be a waste if we are only interested in the first
few keys.
Optimization could mitigate this.
But I would imagine that most people would look at the code and say "that
looks wasteful", because the optimization is not visible or obvious.

If this does not get added to the language, I can live with "=> $_".
PhpStorm already has an option to ignore unused value in foreach(), if a
key exists.
It is not based on the variable name, but this could be added if people ask
for it.

-- Andreas



>
> Cheers,
> Josh


Re: [PHP-DEV] Constructor parent property promotion

2020-08-17 Thread Andreas Hennings
Interesting stuff!
https://wiki.php.net/rfc/constructor_promotion
I see this for the first time, but I like it :)

Now to your proposal.
Just to make sure I understand:
Would this map the parameters by name or by index?
-> I assume by index, it makes more sense.
Does it map to parent constructor parameters, or to parent properties?
-> I assume to parent constructor parameters, because parent properties
could be private.
Can the 'parent' parameters be intermixed with other parameters?
-> I assume yes, not allowing it would be an unnecessary limitation.

So if you use the 'parent' syntax, you need exactly as many parameters with
'parent' as required by the parent constructor.

// Parent class
class B {
  public function __construct($b0, $b1) {
// The values may or may not be written to properties, it does not
matter.
  }
}

// Child class
class C extends B {
  public function __construct(parent $x, private $c0, parent $y, private
$c1) {
// Implicit: parent::__construct($x, $y);
  }
}




On Mon, 17 Aug 2020 at 18:46, Mathieu Rochette  wrote:

> Hi,
>
>
>
> I’m writing a message here to receive feedback on a two ideas related to
> class constructors before going more in details with an RFC. I hope this is
> appropriate behavior for this list
>
>
>
> With the Constructor Property Promotion accepted in PHP 8, there’s a lot
> less of boilerplate when writing class constructors. I’m suggesting two
> additional ways of making thing even easier and I hope, readable.
>
>
>
> First: I’d like to be able to automatically transfer a constructor
> property to its parent constructor. I’m thinking of something like that:
>
>
>
> ```php
>
> class MyCommand extends Command
>
> {
>
>public function __construct(
>
>private Connection $connection,
>
>parent string $name,
>
>) {
>
>}
>
> }
>
> ```
>
>
>
> This would be the equivalent of :
>
>
>
> ```php
>
> class MyCommand extends Command
>
> {
>
>public function __construct(
>
>private Connection $connection,
>
>string $name,
>
>) {
>
>parent::__construct($name);
>
>}
>
> }
>
> ```
>
>
>
> The second idea is to make the constructor body optional, the first example
> would now be:
>
>
>
> ```php
>
> class MyCommand extends Command
>
> {
>
>public function __construct(
>
>private Connection $connection,
>
>parent string $name,
>
>)
>
> }
>
> ```
>
>
>
> This would call the parent constructor automatically even if no "parent"
> parameter are present. I could even see this enforced by linter to avoid
> having logic in the constructors.
>
>
>
> If there is interest I’d like to try writing an rfc and implement it. I
> have not much knowledge of the php-src code but I hope this is a small
> enough change that I’ll be able to tackle. So, at that time if someone is
> interested in mentoring this little project I’d appreciate it :)
>
>
>
>
>
> regards,
>
>
>
> Mathieu


Re: [PHP-DEV] [RFC][Proposal] Renamed parameters

2020-07-27 Thread Andreas Hennings
Good point about the static typing!

On Mon, 27 Jul 2020 at 20:40, Rowan Tommins  wrote:

> Hi Andreas,
>
>
> On 27/07/2020 00:41, Andreas Hennings wrote:
> > 1. Calls with named arguments can only happen on:
> >- explicitly named functions (e.g. no call_user_func()).
> >- constructor calls with an explicitly named class.
> >- object methods, if the object variable is type-hinted or a type is
> > known at compile time.
> > 2. Named arguments in calls are converted to number-indexed arguments at
> > compile time.
> > 3. Parameter name lookup is done based on the type-hinted class or
> > interface. Parameter names on the actual object are ignored.
>
>
> While this is an interesting concept in general, it introduces a much
> larger change to the semantics of the language than seems justified for
> this particular problem - it would effectively require introducing an
> element of "static typing" into the language.
>
> By "static typing", I mean this:
>
>  > variables have an intrinsic type, and the same value can behave
> differently depending on the type of variable that holds it
>
> Which could be contrasted with "dynamic typing" like this:
>
>  > values have an intrinsic type, and operations will be selected based
> on those values, regardless of which variables hold them
>
> Although we have various type annotations in the language now, they are
> all just restrictions on the types of value a variable can hold; they
> don't change the behaviour of that value. With this proposal, these two
> function would potentially do different things when given the exact same
> argument:
>
> function a(FooInterface $arg) { $arg->doSomething(namedparam: 42); }
> function b(BarInterface $arg) { $arg->doSomething(namedparam: 42); }
>

It would be an edge case, if both of them are valid but with
swapped parameter names.
In the more common cases, one of them would simply be broken.
But your point is still correct.

I would argue that this is still better than the behavior with the current
RFC:
The developer who implements the function a or b only knows the parameter
names as in FooInterface or BarInterface.
They know nothing about the actual class of the object passed in as $arg.
A name lookup based on the known interface is closer to what the developer
wants, and is guaranteed to work for all implementations of the interface.

The main inheritance contract for methods is still based on parameter order:
E.g.

interface I {
  function foo(A $x, B $y);
}

interface J extends I {
  function foo(A $y, B $x);
}

The parameter names can be swapped, but the types have to remain in order.


>
>
> Static typing of that sort is a useful feature of other languages, and
> there are people who'd love to see PHP go that way,


Count me in :)


> but it's not
> something we should bolt on in a hurry just to solve an issue with named
> parameters.
>

You have a point.
It would not be a full-blown static typing, but at least it has the smell
of it.
The parameter name lookup would be based on the variable itself, instead of
the value.

As mentioned, there could be other ways to determine the version of the
method that should be used for parameter name lookup.
The options I can come up with atm would make the code more verbose, and/or
look awkward, and probably already clash with existing language features.
But perhaps there are better ways.

Either in the call itself:

$x->{FooInterface::doSomething}(namedparam: 42);
FooInterface::doSomething::invoke($x, namedparam: 42);

Or through a declaration in the file:

use \Acme\FooInterface::*();

$x->doSomething()



> just to solve an issue with named
> parameters.


I still think the current RFC is deeply flawed and should not have been
accepted in its current form.
It adds uncertainty and fragility to the language.
The Liskov substitution principle was something we could rely on,
guaranteed natively by the language. But with named parameters it is not.
I saw the explanations in the RFC about inheritance, but I don't find them
convincing.

But I am not a voting member and also I did not pay attention when this
discussion was happening.
And I am just one person, I don't want to make more than one person's worth
of drama.

Perhaps we will see people mostly use this for constructor or static calls,
and avoid it for polymorphic method calls.
For these cases it seems like a useful feature.


>
>
> Regards,
>
> --
> Rowan Tommins (né Collins)
> [IMSoP]
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>
>


Re: [PHP-DEV] [RFC][Proposal] Renamed parameters

2020-07-27 Thread Andreas Hennings
On Mon, 27 Jul 2020 at 03:18, tyson andre  wrote:

> Hi Andreas Hennings,
>
> > 1. Calls with named arguments can only happen on:
> >   - explicitly named functions (e.g. no call_user_func()).
> >   - constructor calls with an explicitly named class.
> >   - object methods, if the object variable is type-hinted or a type is
> known at compile time.
>
> This proposal would seem to depend on moving opcache into core, among
> other things.
> Depending on how it's implemented, the condition of "a type is known at
> compile time" may also depend on whatever opcache optimization passes were
> enabled,
> which would make the behavior of whether this throws an Error at runtime
> unintuitive to users.
>

Obviously there would need to be a consistent definition about how the type
of a variable should be determined.
And there would need to be a way to determine this at runtime, in case that
opcache is not enabled.
This could be the same system that is responsible for runtime type checks.

Perhaps we should drop the "is known at compile time" and only support
explicit type hints.
Anything we do here should be dumb static analysis, not smart static
analysis.

E.g.

function foo(I $obj) {
  $obj->setColor(color: 'blue');  // Good, based on I::setColor().
  $obj2 = $obj;
  $obj2->setColor(color: 'blue');  // Error, because simple static analysis
cannot determine the type of $obj2.
}


> (e.g. `$a = SOME_OPTIMIZABLE_CONDITION ? new A() : new B();
> $a->someMethod(namedArg: value);`)
>

Any "optimizable condition" would have to be treated like a regular
variable with unknown value.
So in the above case, named arguments would not be allowed.


>
> - `SOME_OPTIMIZABLE_CONDITION` may depend on the php version or
> environment (e.g. `PHP_OS_FAMILY == 'Windows'`)
>
> A recent secondary poll on a rejected RFC I proposed did indicate broad
> interest in moving opcache to php's core,
> but I doubt that'd be done in 8.0 due to the feature freeze, and I also
> doubt optimizations would always be enabled because of the overhead of
> optimization for small short-lived scripts
> ( https://wiki.php.net/rfc/opcache.no_cache#if_you_voted_no_why )
>
> Also, the times when a type is known (with 100% certainty) at compile time
> are known by opcache but unintuitive to users,
> due to the highly dynamic nature of PHP
> (`$$var = 'value'`, references, calls to require()/extract() modifying the
> scope, globals being effectively modifiable at any time, etc.)
> Even for typed properties, the existence of magic methods such as
> `__get()` (e.g. in subclasses) means that the type of $this->prop at
> runtime is uncertain.


> - `__get()` is called if it exists when a declared fetched property (typed
> or otherwise) was unset.
>

Good point.
One thing to note, it seems __get() is only called when the property is
accessed from _outside_.
https://3v4l.org/sY96q (just a snapshot, play around with this as you feel)
I personally don't really care that much about public properties, I could
happily live in a world where named parameters are not available for
objects in public properties.
Or alternatively, we could say that it is the responsibility of the __get()
method to return an object that is compatible with the type hint on the
public property, IF named parameters are used.


Even if all of this does not work, I think the idea still has merit:
Evaluate the parameter names based on a known interface, instead of an
unknown implementation which may come from a 3rd party.
Perhaps a type hint is not the best way to determine this interface..

Greetings
Andreas



>
> Regards,
> - Tyson


Re: [PHP-DEV] [RFC][Proposal] Renamed parameters

2020-07-26 Thread Andreas Hennings
Hello all,

I would like to make my own alternative proposal from my previous message
more concrete.

I am going to describe a version that is not compatible with the existing
RFC at https://wiki.php.net/rfc/named_params.
It _could_ be modified to be an extension to the existing, accepted RFC for
named parameters.
But I think it is useful to discuss this version first.

1. Calls with named arguments can only happen on:
  - explicitly named functions (e.g. no call_user_func()).
  - constructor calls with an explicitly named class.
  - object methods, if the object variable is type-hinted or a type is
known at compile time.
2. Named arguments in calls are converted to number-indexed arguments at
compile time.
3. Parameter name lookup is done based on the type-hinted class or
interface. Parameter names on the actual object are ignored.

Example:

interface I {
  function setBackgroundColor($bgcolor);
}

interface J {
  function setBackgroundColor($background_color);
}

class C implements I, J {
  function setBackgroundColor($color) {}
}

class X {
  private J $colorable;

  function foo(I $colorable) {
// Good: Parameter names from "I::setBackgroundColor()" are used.
$colorable->setBackgroundColor(bgcolor: 'green');

// Error: Unknown parameter name for I::setBackgroundColor().
$colorable->setBackgroundColor(color: 'green');

// Error: Unknown parameter name for I::setBackgroundColor().
$colorable->setBackgroundColor(background_color: 'green');

// Good: Parameter names from "J::setBackgroundColor()" are used.
$this->colorable->setBackgroundColor(background_color: 'green');
  }
}

// Parameter names from C::setBackgroundColor() will be ignored within
->foo().
(new X())->foo(new C());

--

Future extensions:

Allow to define "descriptive pseudo-interfaces" to provide reliable
parameter names, if the original package cannot be trusted to keep
parameter names stable.

interface K describes I {
  function setBackgroundColor($color);
}

function foo(K $colorable) {
  // Parameter names from K::setBackgroundColor() are used.
  $colorable->setBackgroundColor(color: 'green');
}

// Parameter names from C::setBackgroundColor() will be ignored within
foo().
foo(new C());

---

Why?

This version of named parameters can be used with any existing old
interface.
It is compatible with any 3rd party library that may implement an interface
with renamed parameters.
It also allows inheritance from multiple "equivalent" interfaces which use
different parameter names.

Only the package that defines the interface needs to keep the parameter
names stable between versions.

---

How to make this compatible with the existing RFC?

We could introduce an alternative method call syntax for calls that should
take parameter names from a specific interface, instead of the actual class
instance.
Eg.

$obj->{J::setBackgroundColor}(background_color: 'green');

This would be undesirably verbose for long interface names..

-

Best
Andreas






On Fri, 24 Jul 2020 at 18:14, Andreas Hennings  wrote:

> TLDR
> Only consider parameter names from a type-hinted interface, ignore
> parameter names from the actual class.
>
> -
>
> I had a look at
> https://wiki.php.net/rfc/named_params#to_parameter_name_changes_during_inheritance
> Indeed I have concerns about this, because a call with named arguments
> would make your code incompatible with 3rd party implementations of the
> interface that do not keep the parameter names.
> I would see this as a flaw in a newly added feature, which should be fixed
> during the feature freeze.
> I might even say the current version as described in the RFC is broken.
>
> I have no objection to the OP's proposal, but would like to add some
> alternative ideas to the discussion.
>
> My first idea would be to only consider the parameter names in the
> original interface, and ignore all renamed parameters in sub-classes.
> This would cause problems if a class implements multiple interfaces that
> declare the same method, but with different parameter names.
>
> So, a better idea would be like this:
> - The variable must be type-hinted with an interface. E.g. as a parameter
> or as object properties.
> - named arguments always use the parameter names from the type-hinted
> interface.
>
> This would need some static analysis at compile time, and we need to be
> able to type-hint regular local variables.
>
> An alternative would be to somehow "import" the parameter names at the top
> of the file, somehow like so:
>
> use Acme\Animal::bar;  // Parameter names from this method will be used.
>
> (this syntax probably needs more thought).
>
> Yet another option would be to somehow specify the interface in the method
> call..
>
> ((\Acme\Animal) $instance)->foo

Re: [PHP-DEV] [RFC][Proposal] Renamed parameters

2020-07-24 Thread Andreas Hennings
TLDR
Only consider parameter names from a type-hinted interface, ignore
parameter names from the actual class.

-

I had a look at
https://wiki.php.net/rfc/named_params#to_parameter_name_changes_during_inheritance
Indeed I have concerns about this, because a call with named arguments
would make your code incompatible with 3rd party implementations of the
interface that do not keep the parameter names.
I would see this as a flaw in a newly added feature, which should be fixed
during the feature freeze.
I might even say the current version as described in the RFC is broken.

I have no objection to the OP's proposal, but would like to add some
alternative ideas to the discussion.

My first idea would be to only consider the parameter names in the original
interface, and ignore all renamed parameters in sub-classes.
This would cause problems if a class implements multiple interfaces that
declare the same method, but with different parameter names.

So, a better idea would be like this:
- The variable must be type-hinted with an interface. E.g. as a parameter
or as object properties.
- named arguments always use the parameter names from the type-hinted
interface.

This would need some static analysis at compile time, and we need to be
able to type-hint regular local variables.

An alternative would be to somehow "import" the parameter names at the top
of the file, somehow like so:

use Acme\Animal::bar;  // Parameter names from this method will be used.

(this syntax probably needs more thought).

Yet another option would be to somehow specify the interface in the method
call..

((\Acme\Animal) $instance)->foo(a: 'A', b: 'B');

Regards
Andreas



On Fri, 24 Jul 2020 at 17:41, Sara Golemon  wrote:

> On Fri, Jul 24, 2020 at 10:10 AM Nikita Popov 
> wrote:
> > > > You added PHP 8.0 as a proposed version, but that will not be
> possible
> > > > anymore 2 weeks of discussion + 2 weeks of voting are not possible to
> fit
> > > > in before the feature freeze, which is in 11 days.
> > >
> > > While you are technically correct, the primary point of a feature
> freeze
> > > is not allowing in completely new features.
> > > It will always happen that there are changes and extensions to RFCs
> > > introduced for the next version which may need to be addressed first,
> > > because there is massive benefit to the new feature in that case. (from
> a
> > > backwards/forwards compatibility standpoint for example)
> > >
> > We should of course be open to making minor adjustments due to
> > unanticipated issues after feature freeze -- after all, that's where we
> > gain experience with new features. The emphasis here is very much on
> > *minor* and *unanticipated* though.
> >
>
> Endorsing this.  Anything post FF needs to be held to a high standard.
> Minor and Unanticipated changes.
> * The option to set alt-names on parameters is substantial.
>  * Making named params explicitly opt-in is less substantial (though
> certainly not trivial), especially if it were hung off an attribute which
> would mean no actual new syntax, but Niki's point that it wasn't an unknown
> or unanticipated issue stands.
>
> I share OP's worry that this pushes workload onto library and framework
> maintainers, and I wouldn't look forward to the review of all APIs to make
> sure their names are correct, but the vote has spoken.  We should
> absolutely continue pursuing this topic with an eye to 8.1 so that library
> updates are less painful moving forward.
>
> -Sara
>


Re: [PHP-DEV] [RFC] [DISCUSSION] Make constructors and destructors return void

2020-06-18 Thread Andreas Hennings
On Thu, 18 Jun 2020 at 22:29, Benas IML  wrote:

> Hey Andreas,
>
> It seems that you actually haven't read my reply carefully enough.
>
> > But this distinction would only apply in PHP 8. And the only difference
> here
> is whether returning a value is just deprecated or fully illegal.
> > In PHP 9, constructors with ": void" would be the same as without ":
> void".
> So long term it will become a "meaningless choice".
> >
> > Or what am I missing?
>
> The "allowance" of `void` return type will help:
> - to be more explicit.
> - to enforce `void` rules on constructors/destructors (in PHP 8).
> - to be more consistent with other methods (which is what people like the
> most
> about allowing `void` on ctor/dtor based on r/php).
> - to be more consistent with the documentation.
>
> > Except consistency with existing constructors in other packages which
> choose
> to not add ": void" to constructors.
> >
> > Either it is enforced in a "code convention", or it is up to every single
> developer and team, and we get silly arguments between developers in code
> review whether or not this should be added. Or we get git noise because one
> developer adds those declarations, and another removes them.
>
> I find these comments of yours to make completely no sense since most of
> the
> additions to PHP will create some sort of silly arguments between
> developers.
>
> A best example would be the `mixed` type. Based on what you have said, this
> pseudo type will create inconsistencies because some libraries will have
> parameters explicitly declared as `mixed` while others - won't. It will
> also
> create arguments between developers on whether they should use it or not.
>
> Moving on, let's take the `void` type (implemented in PHP 7.1) as another
> great
> example! Laravel and Symfony (both depend on PHP 7.2) don't use it while
> other
> libraries - do. So, as I understand based on your comments, this is
> creating
> inconsistencies and arguments between developers. Some say `void` should be
> added, some say not. Some libraries declare it, some don't.
>

Another example would be the "public" access specifier, which can be
omitted and the method will still be public.

There is a major difference though:

If you look at a method without "public", or a parameter without "mixed",
there is a chance that the developer actually should have put something
else here, e.g. "private" or "string", and was simply too lazy, was not
aware of that possibility, or wrote the code with a prior PHP version in
mind.
Having the explicit keyword documents an intentional choice to make the
method public, to make the parameter mixed, etc.

On the other hand, for a constructor the ": void" is just stating the
obvious.
Even if you see a constructor without ": void", you still know that this is
not meant to return anything.
Either because it is generally agreed to be bad (PHP7) or because it is
deprecated (PHP8) or because it is illegal (PHP9)



>
> Moving on, attributes. If you go onto Reddit, you can see that the crowd is
> divided 50/50. Some believe that attributes are bad and say that they will
> use
> functions (for "adding" metadata) instead. Others prefer attributes and
> will
> use those. You can literally find people bashing each other for their
> preference. So that is creating heated arguments AND possibly future
> inconsistencies between different people/libraries.
>

But here there are actual technical arguments why someone might prefer one
or the other solution.


>
> It's not possible to be "politically" neutral. Every single feature/code
> style
> is used by some group of people and isn't used by another. So next time
> please
> be more pragmatic.
>
> > So if you want to support PHP 7, you cannot add ": void".
> > If you only care about PHP 9, you don't need to add ": void" because it
> is
> pointless.
> >
> > Any convention would probably discourage it to be more universally
> usable.
>
> This is a completely contradicting statement. Just a few sentences ago you
> said
> that there will be silly arguments between people. But now as I understand,
> most conventions will probably discourage the explicit `: void` return
> type on
> constructors/destructors. Thus, since most people follow conventions, there
> will be little-to-no arguments.
>

There are conventions or popular preference, and there are people or
projects which want to do things their own way.
And of course there is the time before agreements are reached in specific
conventions, where people produce code which could be o

Re: [PHP-DEV] [RFC] [DISCUSSION] Make constructors and destructors return void

2020-06-18 Thread Andreas Hennings
On Thu, 18 Jun 2020 at 18:33, Benas IML  wrote:

> Hey Andreas,
>
> That is incorrect. Adding an explicit `: void` does change behavior since
> only then the check (whether something is being returned) is enforced. This
> allows PHP 8 projects to take advantage of this enforcement while being
> respective to older codebases.
>
> Ok. I read more carefully now.

But this distinction would only apply in PHP 8. And the only difference
here is whether returning a value is just deprecated or fully illegal.
In PHP 9, constructors with ": void" would be the same as without ": void".
So long term it will become a "meaningless choice".

Or what am I missing?



> And well, the explicit `: void` declaration also helps your code to be
> more consistent with other methods ;)
>

Except consistency with existing constructors in other packages which
choose to not add ": void" to constructors.


>
> Without an explicit `: void` return type declaration, `void` rules are not
> enforced on constructors/destructors and will not be until PHP 9.0 (which
> will probably release in 5 years).
>
> Moreover, saying "it forces organisations, frameworks or the php-fig group
> to introduce yet another coding convention" is a complete exaggeration. In
> fact, even now there are no PSR conventions that specify how and when to
> write parameter/return/property type hints.
>

Either it is enforced in a "code convention", or it is up to every single
developer and team, and we get silly arguments between developers in code
review whether or not this should be added. Or we get git noise because one
developer adds those declarations, and another removes them.


>
> Also, it's important to understand that PHP libraries are really really
> slow at starting to **depend** on new PHP versions. It will probably take a
> few good years (if not more) until first few libraries start requiring PHP
> 8.0. As a matter of fact, even Symfony framework is still requiring PHP
> 7.2.5 and cannot take advantage of its newer features (e. g. typed
> properties).
>

So if you want to support PHP 7, you cannot add ": void".
If you only care about PHP 9, you don't need to add ": void" because it is
pointless.

Any convention would probably discourage it to be more universally usable.



>
> Last but not least, just to reiterate, the `: void` return type is
> optional and you don't need to specify it.
>

Exactly my point. "Optional" means people will make arbitrary choices and
argue about it, or look for a convention.

Greetings
Andreas



>
> Best regards,
> Benas
>
> On Thu, Jun 18, 2020, 7:02 PM Andreas Hennings 
> wrote:
>
>> Hi
>>
>> The RFC introduces what I call a "meaningless choice", by making
>> something possible that is currently illegal, but which does not change
>> behavior.
>> https://3v4l.org/daeEm
>>
>> It forces organisations, frameworks or the php-fig group to introduce yet
>> another coding convention to decide whether or not there should be a ":
>> void" declaration on constructors.
>>
>> I am ok with restricting the use of "return *;" inside a constructor.
>> But I don't like allowing the ": void" declaration.
>>
>> Greetings
>>
>> On Thu, 18 Jun 2020 at 17:18, Benas IML 
>> wrote:
>>
>>> Hey Bob,
>>>
>>> Magic methods are **never** supposed to be called directly (even more if
>>> that method is a constructor or a destructor). If that's not the case,
>>> it's
>>> just plain bad code. But by enforcing these rules, we make sure that less
>>> of that (bad code) is written and as a result, we make PHP code less
>>> bug-prone and easier to debug. That's also most likely the reason why
>>> "ensure magic methods' signature" RFC opted in to validate `__clone`
>>> method's signature and ensure that it has `void` return type.
>>>
>>> Just for the sake of making sure that you understand what I mean, here
>>> are
>>> a couple of examples that show that no magic method is ever supposed to
>>> be
>>> called directly:
>>> ```php
>>> // __toString
>>> (string) $object;
>>>
>>> // __invoke
>>> $object();
>>>
>>> // __serialize
>>> serialize($object);
>>> ```
>>>
>>> Moreover, by validating constructors/destructors and allowing an explicit
>>> `void` return type declaration, we are becoming much more consistent
>>> (something that PHP is striving for) with other magic methods (e. g.
>>> `__clone`).
>>>
>>>

Re: [PHP-DEV] [RFC] [DISCUSSION] Make constructors and destructors return void

2020-06-18 Thread Andreas Hennings
Hi

The RFC introduces what I call a "meaningless choice", by making something
possible that is currently illegal, but which does not change behavior.
https://3v4l.org/daeEm

It forces organisations, frameworks or the php-fig group to introduce yet
another coding convention to decide whether or not there should be a ":
void" declaration on constructors.

I am ok with restricting the use of "return *;" inside a constructor.
But I don't like allowing the ": void" declaration.

Greetings

On Thu, 18 Jun 2020 at 17:18, Benas IML  wrote:

> Hey Bob,
>
> Magic methods are **never** supposed to be called directly (even more if
> that method is a constructor or a destructor). If that's not the case, it's
> just plain bad code. But by enforcing these rules, we make sure that less
> of that (bad code) is written and as a result, we make PHP code less
> bug-prone and easier to debug. That's also most likely the reason why
> "ensure magic methods' signature" RFC opted in to validate `__clone`
> method's signature and ensure that it has `void` return type.
>
> Just for the sake of making sure that you understand what I mean, here are
> a couple of examples that show that no magic method is ever supposed to be
> called directly:
> ```php
> // __toString
> (string) $object;
>
> // __invoke
> $object();
>
> // __serialize
> serialize($object);
> ```
>
> Moreover, by validating constructors/destructors and allowing an explicit
> `void` return type declaration, we are becoming much more consistent
> (something that PHP is striving for) with other magic methods (e. g.
> `__clone`).
>
> Also, saying that "sometimes you have valid information to pass from the
> parent class" is quite an overstatement. After analyzing most of the 95
> Composer packages that had a potential BC break, I found out that either
> they wanted to return early (that is still possible to do using `return;`)
> or they added a `return something;` for no reason. Thus, no libraries
> actually returned something useful and valid from a constructor (as they
> shouldn't).
>
> Last but certainly not least, constructors have one and only one
> responsibility - to initialize an object. Whether you read Wikipedia's or
> PHP manual's definition, a constructor does just that. It initializes. So,
> the PHP manual is perfectly correct and documents the correct return type
> that a constructor should have.
>
> Best regards,
> Benas
>
> On Thu, Jun 18, 2020, 4:06 PM Bob Weinand  wrote:
>
> > > Am 17.06.2020 um 01:10 schrieb Benas IML :
> > >
> > > Hey internals,
> > >
> > > This is a completely refined, follow-up RFC to my original RFC. Based
> on
> > the
> > > feedback I have received, this PR implements full validation and
> > implicitly
> > > enforces `void` rules on constructors/destructors while also allowing
> to
> > > declare an **optional** explicit `void` return type. Note, that there
> is
> > a
> > > small but justifiable BC break (as stated by the RFC).
> > >
> > > RFC: https://wiki.php.net/rfc/make_ctor_ret_void
> > >
> > > Best regards,
> > > Benas Seliuginas
> >
> > Hey Benas,
> >
> > I do not see any particular benefit from that RFC.
> >
> > Regarding what the manual states - the manual is wrong there and thus
> > should be fixed in the manual. This is not an argument for changing
> engine
> > behaviour.
> >
> > Sometimes a constructor (esp. of a parent class) or destructor may be
> > called manually. Sometimes you have valid information to pass from the
> > parent class.
> > With your RFC an arbitrary restriction is introduced necessitating an
> > extra method instead.
> >
> > In general that RFC feels like "uh, __construct and __destruct are mostly
> > void, so let's enforce it … because we can"?
> >
> > On these grounds and it being an additional (albeit mostly small)
> > unnecessary BC break, I'm not in favor of that RFC.
> >
> > Bob
>


Re: [PHP-DEV] Make sorting stable

2020-03-04 Thread Andreas Hennings
Note that a comparison callback can also be broken in other ways:
- It could contain a loop, e.g. 'a' < 'b', 'b' < 'c', 'c' < 'a'.
- It could have alternating return values in subsequent calls with the same
arguments.

This kind of misbehavior cannot be easily detected.
The best we can do is make sure these kinds of comparison functions do not
cause infinite loops.

This said: Yes, stable sort out of the box would be a welcome feature.

On Wed, 4 Mar 2020 at 20:49, Larry Garfield  wrote:

> On Wed, Mar 4, 2020, at 12:35 PM, Sara Golemon wrote:
> > On Wed, Mar 4, 2020 at 12:12 PM Nikita Popov 
> wrote:
> >
> > > On Wed, Mar 4, 2020 at 6:17 PM Sara Golemon  wrote:
> > >
> > >> On Wed, Mar 4, 2020 at 10:42 AM Nikita Popov 
> > >> wrote:
> > >>
> > >>> The only issue I ran into is that this change has a negative impact
> on
> > >>> code
> > >>> using illegal comparison callbacks like this:
> > >>>
> > >>> usort($array, function($a, $b) {
> > >>> return $a > $b;
> > >>> });
> > >>>
> > >>> Let's define what "negative impact" means in this regard.  Is it that
> > >> one still winds up with an essentially sorted array, but hitherto
> "stable
> > >> appering" output is now stable in a different way?  Or is the result
> > >> actually just NOT sorted in a way that a reasonable user would
> consider
> > >> correct (e.g. 5 sorted before "3")?
> > >>
> > >
> > > "Negative impact" is PR speak for "completely broken" ;) Yes, it could
> > > sort 5 before 3. The comparison function becomes completely
> ill-defined and
> > > you get Garbage-In-Garbage-Out.
> > >
> > > Roger that, thanks for explaining. 
> >
> >
> > > If we are concerned about this (I'm not sure we should be, but I've at
> > > least seen people be interested about this peculiar behavior:
> > >
> https://stackoverflow.com/questions/59908195/how-come-usort-php-works-even-when-not-returning-integers
> ),
> > > there's two things we can do:
> > >
> > > 1. As Tyson suggests, we can throw a warning if a boolean is returned
> from
> > > the comparison callback (probably with a check to only throw it once
> per
> > > sort), to make it obvious what the cause is.
> > >
> > > 2. We can fix this transparently by doing something like this
> internally:
> > >
> > > $result = $compare($a, $b);
> > > if ($result !== false) {
> > > return (int) $result; // Normal behavior
> > > }
> > >
> > > // Bad comparison function, try the reverse order as well
> > > return -$compare($b, $a);
> > >
> > >
> > ¿Por que no los dos?
> >
> > Fixing broken stuff transparently for a smooth upgrade path PLUS letting
> > people know they're doing it wrong seems win-win and low cost.
> >
> > -Sara
>
>
> I concur.  If a comparison function returns a non-legal value:
>
> 1) Fold it to a legal value. if feasible.
> 2) Raise a notice/warning, maybe deprecated? that tells them they're Doing
> It Wrong(tm)
> 3) Sometime in the future turn that notice/warning into a hard error.
>
> If I'm understanding the definition of stable here, it means that if two
> values evaluate to equal they will always end up in the same order in the
> output that they were in the input, yes?  So (trivial example):
>
> $a = ["3", 2, 3, 5];
>
> Sorts to:
>
> [2, "3", 3, 5];
>
> always, whereas right now it may sort to that or to [2, 3, "3", 5],
> somewhat randomly.  Am I understanding correctly?
>
> --Larry Garfield
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>
>


Re: [PHP-DEV] [RFC] [DISCUSSION] Immutable/final/readonly properties

2020-02-21 Thread Andreas Hennings
When writing immutable classes, I want to be able to set properties in
static factories and in wither methods.

Once the new instance is sent to the outside world, its properties can be
locked to prevent further modification.

This sounds to me like we need different modes. Either the object itself
would have different states over time, or the object stays the same and
instead some methods have mutation permission on newly created objects.

This could be seen as a runtime state problem or as a compile time code
verification problem.

On Sat, 22 Feb 2020, 00:18 Larry Garfield,  wrote:

> On Fri, Feb 21, 2020, at 4:29 AM, Máté Kocsis wrote:
> > >
> > > Yeah, I'm definitely thinking in relation to the earlier discussion,
> since
> > > I think they're all inter-related.  (This, property accessors, and
> constant
> > > expressions.)
> > >
> >
> > The biggest question is whether it's worth to support both readonly
> > properties and property accessors. My answer is clear yes, because there
> > are many-many
> > ways to mess with private or protected properties without and public
> > setters from the outside - in which case property accessors couldn't help
> > much. I collected some examples I know of:
> > https://3v4l.org/Ta4PM
>
> I didn't even know you could do some of those.  That's horrifying. :-)
>
> > > As Nikita notes above, a read-only property with a default value is...
> > > basically a constant already.  So that's not really useful.
> >
> > I agree that they are not very useful, however I wouldn't restrict their
> > usage. Mainly because there are probably some legitimate use-cases, but I
> > also think it would
> > be advantageous to be consistent with the other languages in this case.
>
> If they could do something that class constants can't, that would make
> them useful.  If not, then I feel like it would just be introducing new
> syntax for the same thing, without much benefit.  (I'm thinking of, eg,
> could you set them by default to a new Foo() object, which you could then
> modify the Foo but not change it for another object, thus moving that
> initialization out of the constructor?  That sort of thing.)
>
> > > If we could address the performance impact, that would give us much
> more
> > > functionality-for-the-buck, including an equivalent of read-only
> properties
> > > including potentially lazy initialization.  Or derive-on-demand
> behavior
> > > would also be a big increase in functionality.
> > >
> > > It's not that I don't see a value to this RFC; I actually have a few
> > > places in my own code where I could use it.  It's that I see it as
> being of
> > > fairly narrow use, so I'm trying to figure out how to increase it so
> that
> > > the net-win is worth it.
> > >
> >
> > The reason why I brought up this RFC is that I'd really like to add
> > first-class support for immutable objects, and it seemed to be a good
> idea
> > to first go for readonly properties.
> > This way, the scope of an immutable object RFC gets smaller, while it's
> > possible to only have readonly properties alone.
> >
> > Regards,
> > Máté
>
> I'm totally on board for better value object support, so that's a good
> motive for me.  The question I have is whether this is really a good
> stepping stone in that direction or if it would lead down a wrong path and
> lock us into too much TIMTOWTDI (for the Perl fans in the room).  So let's
> think that through down that path.  How would write-once properties lead
> into properly immutable value objects?  Or do they give us that themselves?
>
> The biggest challenge for immutable objects, IMO, is evolving them.  Eg,
> $result->withContentType(...) to use the PSR-7 example.  Would we expect
> people to do it with a method like that, or would there be some other
> mechanism?  If the properties are public, would we offer a more syntactic
> way to modify them directly?
>
> The with*() method style requires cloning the object.  What happens to the
> locked status of a set property if the object is cloned?  Are they then
> settable again, or do they come pre-locked?
>
> Neither of those seem good, now that I think about it.  If they come
> pre-locked, then you really can't clone, change one property, and return
> the new one (as is the standard practice now in that case).  If they don't
> come pre-locked, then the newly created object can have everything on it
> changed, once, which creates a loophole.  I'm not sure what the right
> answer is here.
>
> My other concern is a public property (the most likely use case) would
> have to be set in the constructor.  If it's not, then callers cannot rely
> on it having been set yet if it's set lazily.  And if code inside the class
> tries to set it lazily, it may already have been set by some external code
> (rightly or wrongly) and cause a failure.
>
> How do we address that?  There's absolutely use cases where setting
> everything in the constructor ahead of time is what you'd do anyway, but
> there are plenty where you 

Re: [PHP-DEV] Re: [RFC] "arrayable" pseudo type hint

2020-02-04 Thread Andreas Hennings
On Tue, 4 Feb 2020 at 22:19, Larry Garfield  wrote:

> On Tue, Feb 4, 2020, at 12:40 PM, Aimeos | Norbert Sendetzky wrote:
> > Am 04.02.20 um 19:17 schrieb Rowan Tommins:
> > > I think Larry's point was that the flexibility of PHP's array type
> makes it
> > > really hard to pin down whether a given object is "array-like" or not,
> and
> > > which attributes a particular function actually cares about.
> >
> > What else besides array access, counting and traversing is possible that
> > may differ from classes that implement those interfaces?
> >
> > > A general "intersection type" system might be more useful, because
> then you
> > > could require the parts you specifically needed, such as
> > > "traversable" or "traversable".
> >
> > I think that's too complicated and we should make it as easy as possible
> > for PHP developers.
> >
> > Also, there's already an RFC for intersection types but it was never
> > adopted: https://wiki.php.net/rfc/intersection_types
>
>
> Rowan is exactly right and said it better than I did.
>
> The point is that "I can count() it", "I can foreach() it" and "I can
> bracket it" are three different things; in practice, a given function
> likely only cares about one, maybe two of those at a time.  Adding a type
> for "an object that mimics all of the dumb things arrays do, but now passes
> differently" doesn't strike me as useful; it strikes me as the sort of
> thing I'd reject in a code review if someone tried to do it in user space.
>
> The problem with PHP arrays is that they're not arrays; they're a hash map
> with poor safety, lame error semantics, and some cheats to make them kinda
> sorta look like arrays if you don't look too carefully.  In practice, they
> create more bugs than they fix.
>

There is one good thing about arrays:
They are passed along by-value by default, which gives them similar
qualities as an "immutable" object.
If you pass an array to a function as a parameter which is not
by-reference, you can expect the original array to remain unchanged.
(Objects or referenced variables within that array can still be modified of
course)

A function parameter which allows an object OR an array loses this
advantage.

If I have the choice between \stdClass and array for "unstructured tree of
data", I will always prefer the array.

~~ Andreas


>
> --Larry Garfield
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>
>


Re: [PHP-DEV] [RFC] Static return type

2020-01-10 Thread Andreas Hennings
On Thu, 9 Jan 2020 at 20:53, Dik Takken  wrote:

> On 09-01-2020 17:52, Andreas Hennings wrote:
> > On Thu, 9 Jan 2020 at 16:31, Nikita Popov  wrote:
> >
> >> On Thu, Jan 9, 2020 at 4:06 PM Rowan Tommins 
> >> wrote:
> >>
> >> An argument could be made that $this does also specify a certain
> contract
> >> to the caller: That the API may be used fluently or not without change
> in
> >> functionality. That is
> >>
> >> $foo->setBar();
> >> $foo->setBaz();
> >>
> >> must be strictly equivalent to
> >>
> >> $foo
> >>->setBar()
> >>->setBaz()
> >> ;
> >>
> >> The same is not the case for a plain "static" type. In fact, I think
> that
> >> for all other uses of "static" outside of fluent interfaces, not using
> the
> >> return value would be a programming error.
> >>
> >> But still, this kind of contract is not a type-system contract, and I'm
> not
> >> sure it's a good idea to mix other types of API contracts into the type
> >> system in this fashion.
> >>
> >
> > We currently don't have another "system" where this kind of info could be
> > specified.
>
> How about:
>
>   public fluent function doWhatever(): static
>
> Perhaps this could be introduced at some point in the future to enforce
> returning $this.
>

This would work, but:
- The ": static" would be redundant if the method is fluid.
- I still would find it more natural to look into the return hint and find
": $this". With fluent + static I would have to look in two places.

But I was thinking something else related to the ": $this" (or "fluent").
If we already annotate a method as fluent, then the actual "return $this;"
statement is redundant as well.
We _could_ make this implicit, so you no longer have to write "return
$this;".
Not sure if that's a good idea, and definitely not in scope of the ":
static" proposal. But I think it is useful to have an idea where we want to
go with this.




>
> > And in this case, $this also implies static, so if we would use whichever
> > other system to specify $this, the type hint "static" would become
> > redundant.
> >
> > "@return $this" is common in phpdoc (*) since a while, and so far I have
> > not seen any problems with it.
> >
> > If we ask "where does this lead?", one natural next step might be to
> > specifically say that the return type is the same type, but NOT $this.
> > So, a promise that we really get a new object which we can modify without
> > changing the original object.
> >
> > I don't know how we would express this. Perhaps ": clone", but this would
> > be too specific imo, because whether it is a clone is an implementation
> > detail.
>
> Maybe ": new" ?
>

To me this would not imply that this has to be the same type..

Either way, I think there is no strong enough reason to introduce a syntax
for this case.
It would be mostly for wither methods, and these can safely return the
original object if the entire thing is designed as immutable.


>
> Regards,
> Dik Takken
>


Re: [PHP-DEV] [RFC] Static return type

2020-01-09 Thread Andreas Hennings
On Thu, 9 Jan 2020 at 16:31, Nikita Popov  wrote:

> On Thu, Jan 9, 2020 at 4:06 PM Rowan Tommins 
> wrote:
>
> > On Thu, 9 Jan 2020 at 14:23, Andreas Hennings 
> wrote:
> >
> > > > However, $this is not a real type, and it is unclear what the
> advantage
> > > of specifying $this rather than static would be from a type system
> level
> > > perspective.
> > >
> > > Perhaps not from a "type system level", but from a more broad "enforced
> > > contract" level.
> > > E.g. IDEs or code inspection tools can warn if a method will not return
> > > $this.
> > > This also means recursive calls also need to return $this instead of
> > > static.
> > >
> > > class C {
> > >   function foo(): static {
> > > return clone $this;
> > >   }
> > >   function bar(): $this {
> > > return $this->foo();  // IDE can complain.
> > >   }
> > > }
> > >
> >
> >
> > I think there are two different purposes for annotating return types:
> >
> > 1) Contracts that tell the caller what values they should expect when
> > calling the function.
> > 2) Constraints on the implementation which don't affect the caller, but
> > allow the author to catch certain bugs.
> >
> > The primary purpose, in my mind, is (1), with (2) generally coming as a
> > side-effect: if your contract is to return an int, then tools can warn
> you
> > when you don't.
> >
> > All concrete types can be seen this way: scalars, classes, and
> pseudo-types
> > like "iterable" are all contracts that calling code can rely on. "self",
> > "parent", and "static" fit into that list fine, because they're
> ultimately
> > specifying a class name.
> >
> > The special "void" keyword doesn't actually make sense as a contract
> given
> > PHP's calling convention - as far as the calling code's concerned, it's
> > equivalent to "null". So it exists only for purpose (2), constraining the
> > implementation for the benefit of the function's author.
> >
> > $this would fit into the same category - as far as calling code is
> > concerned it is equivalent to "static", unless they're doing something
> very
> > specific with object identity. This makes it an odd constraint on
> > interfaces, for example - DateTimeInterface exists specifically to have
> > mutable and immutable implementations, and a return type of $this would
> > explicitly prevent that.
> >
> > If we add ": $this" alongside ": void", I wonder where that takes us
> next -
> > what other constraints on a function can be expressed using that notation
> > which aren't contracts on the value seen by the caller? If we don't want
> to
> > explore that question, should we avoid adding ": $this"?
> >
>
> An argument could be made that $this does also specify a certain contract
> to the caller: That the API may be used fluently or not without change in
> functionality. That is
>
> $foo->setBar();
> $foo->setBaz();
>
> must be strictly equivalent to
>
> $foo
>->setBar()
>->setBaz()
> ;
>
> The same is not the case for a plain "static" type. In fact, I think that
> for all other uses of "static" outside of fluent interfaces, not using the
> return value would be a programming error.
>
> But still, this kind of contract is not a type-system contract, and I'm not
> sure it's a good idea to mix other types of API contracts into the type
> system in this fashion.
>

We currently don't have another "system" where this kind of info could be
specified.
And in this case, $this also implies static, so if we would use whichever
other system to specify $this, the type hint "static" would become
redundant.

"@return $this" is common in phpdoc (*) since a while, and so far I have
not seen any problems with it.

If we ask "where does this lead?", one natural next step might be to
specifically say that the return type is the same type, but NOT $this.
So, a promise that we really get a new object which we can modify without
changing the original object.

I don't know how we would express this. Perhaps ": clone", but this would
be too specific imo, because whether it is a clone is an implementation
detail.

In phpdoc (*) there is no common syntax for "static, but not $this", so
perhaps it was not needed yet.

And in fact, an immutable wither can safely return the original object if
no changes are needed, if other methods are also immutable.

(*) When I say "phpdoc", I really mean the kinds of docs that people
commonly use in the wild, and that are understood by IDEs.
Which of that is documented and supported by the phpdoc library is a
different question.



>
> Regards,
> Nikita
>


Re: [PHP-DEV] [RFC] Static return type

2020-01-09 Thread Andreas Hennings
On Thu, 9 Jan 2020 at 15:22, Andreas Hennings  wrote:

> > However, $this is not a real type, and it is unclear what the advantage
> of specifying $this rather than static would be from a type system level
> perspective.
>
> Perhaps not from a "type system level", but from a more broad "enforced
> contract" level.
> E.g. IDEs or code inspection tools can warn if a method will not return
> $this.
> This also means recursive calls also need to return $this instead of
> static.
>

> class C {
>   function foo(): static {
> return clone $this;
>   }
>   function bar(): $this {
> return $this->foo();  // IDE can complain.
>   }
> }
>


Of course this ": $this" could be added later in the future.
But this would have the risk that people start adding ": static" on methods
that really return $this.
This would cause confusion if later some packages introduce ": $this", but
others are still behind.

namespace oldpackage;
class B {
  function foo(): static { // Actually returns $this, not just static.
return $this;
  }
}

namespace newpackage;
class C extends B {
  function foo(): $this {
return parent::foo();  // Should the IDE complain?
  }
}


>
>
> On Thu, 9 Jan 2020 at 15:07, Larry Garfield 
> wrote:
>
>> On Wed, Jan 8, 2020, at 5:42 AM, Nikita Popov wrote:
>> > Hi internals,
>> >
>> > I would like to propose the following RFC, which allows using "static"
>> as a
>> > return type:
>> >
>> > https://wiki.php.net/rfc/static_return_type
>> >
>> > While I'm personally not a fan of late static binding, we do support it
>> and
>> > people do use it quite heavily, so I think we should also support it in
>> > return types.
>> >
>> > Regards,
>> > Nikita
>>
>>
>> Is "squee!" an appropriate response on the list?  For interface authors
>> this is a huge deal.
>>
>> --Larry Garfield
>>
>> --
>> PHP Internals - PHP Runtime Development Mailing List
>> To unsubscribe, visit: http://www.php.net/unsub.php
>>
>>


Re: [PHP-DEV] [RFC] Static return type

2020-01-09 Thread Andreas Hennings
> However, $this is not a real type, and it is unclear what the advantage
of specifying $this rather than static would be from a type system level
perspective.

Perhaps not from a "type system level", but from a more broad "enforced
contract" level.
E.g. IDEs or code inspection tools can warn if a method will not return
$this.
This also means recursive calls also need to return $this instead of static.

class C {
  function foo(): static {
return clone $this;
  }
  function bar(): $this {
return $this->foo();  // IDE can complain.
  }
}


On Thu, 9 Jan 2020 at 15:07, Larry Garfield  wrote:

> On Wed, Jan 8, 2020, at 5:42 AM, Nikita Popov wrote:
> > Hi internals,
> >
> > I would like to propose the following RFC, which allows using "static"
> as a
> > return type:
> >
> > https://wiki.php.net/rfc/static_return_type
> >
> > While I'm personally not a fan of late static binding, we do support it
> and
> > people do use it quite heavily, so I think we should also support it in
> > return types.
> >
> > Regards,
> > Nikita
>
>
> Is "squee!" an appropriate response on the list?  For interface authors
> this is a huge deal.
>
> --Larry Garfield
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>
>


Re: [PHP-DEV] [RFC] "arrayable" pseudo type hint

2019-11-25 Thread Andreas Hennings
On Sun, 24 Nov 2019 at 16:55, CHU Zhaowei  wrote:

> > This proposal is not obsolete by the implementation of union types (i.e.
> > array|Traversable) because union data types are types/interfaces
> > combined by OR. "arrayable" is a combination of OR and AND:
> >
> > array|ArrayAccess
>
> True. But it's also easy to implement in userland. Just create a new
> interface that extends these three interfaces, then union types can work.
>
> ```php
> interface Arrayable extends ArrayAccess, Countable, Traversable {}
>
> function array_x (array|Arrayable $arr) {}
> ```
>
> This is not something that userland cannot implement or very difficult to
> implement. IMO it will make php more complex if we include this in the
> core. Actually what you suggested has been totally covered by the future
> scopes of the union types RFC.


or "intersection types":
https://wiki.php.net/rfc/intersection_types


> If you really want to explore this idea, I think they are worth more
> attention, instead of this specific case.
>
> Regards,
> CHU Zhaowei
>
> > -Original Message-
> > From: Aimeos | Norbert Sendetzky 
> > Sent: Wednesday, November 20, 2019 6:21 AM
> > To: internals@lists.php.net
> > Subject: [PHP-DEV] [RFC] "arrayable" pseudo type hint
> >
> > Since PHP 7.1, there's the "iterable" pseudo type hint that matches
> "array" or
> > "Traversable".
> >
> > PHP frameworks would profit from support of an "arrayable" pseudo type
> hint
> > that matches "array" and all objects that can be used like arrays.
> >
> > For that, "arrayable" objects have to implement these interfaces:
> > - ArrayAccess
> > - Countable
> > - Traversable (i.e. either Iterator or IteratorAggregate)
> >
> >
> > Implementing "arrayable" pseudo type, we could pass arrays or all
> objects that
> > can be used like arrays to methods and do:
> >
> > function useArrayable( arrayable $arg ) : arrayable {
> > $cnt = count( $arg );
> > $value = $arg['key'];
> > foreach( $arg as $key => $entry );
> > return $arg;
> > }
> >
> >
> > Best use cases are:
> >
> > - Laravel Collection
> > (
> https://github.com/laravel/framework/blob/6.x/src/Illuminate/Support/Collect
> > ion.php)
> >
> > - Aimeos Map (https://github.com/aimeos/map)
> >
> >
> > No new interface is proposed because we can check if objects implement:
> >
> > ArrayAccess && Countable && Traversable
> >
> >
> > Because "array" !== "arrayable", "arrayable objects will not be accepted
> by
> > array_* functions and all functions that only use "array" as type hint
> for
> > parameters and return types.
> >
> >
> > This proposal is not obsolete by the implementation of union types (i.e.
> > array|Traversable) because union data types are types/interfaces
> > combined by OR. "arrayable" is a combination of OR and AND:
> >
> > array|ArrayAccess
> >
> >
> > Also, "arrayable" won't supersede "iterable" because
> >
> > - "iterable" matches arrays, iterators and generators
> >
> > - "arrayable" matches arrays and objects that can be used like arrays
> >
> >
> > "Can be used like arrays" doesn't include support for empty() because it
> works
> > for empty arrays but not for "arrayable" objects without elements.
> > If possible and requested, this may be addressed by another RFC.
> >
> >
> > As Larry Garfield suggested, this pre-RFC proposes concentrates on the
> > "arrayable" pseudo type only.
> >
> > It does NOT discusses that:
> >
> > - arrayable objects can be converted to arrays (Steven Wade works on an
> RFC
> > for a __toArray() magic function)
> >
> > - ArrayObject or any other arrayable object makes the array_* functions
> less
> > relevant in the future
> >
> > - arrayable objects can be passed to array_* functions in the future
> >
> > --
> > PHP Internals - PHP Runtime Development Mailing List To unsubscribe,
> visit:
> > http://www.php.net/unsub.php
> >
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>
>


Re: [PHP-DEV] Optional pre-compiler for PHP8?

2019-10-30 Thread Andreas Hennings
On Wed, 30 Oct 2019 at 22:06, Rowan Tommins  wrote:
>
> Hi Dik,
>
> On 29/10/2019 21:44, Dik Takken wrote:
> > Opcache already performs type inference. [...]
> > Here is an interesting read on the subject:
> >
> > https://depositonce.tu-berlin.de/bitstream/11303/7919/3/popov_etal_2017.pdf
>
>
> Thanks for the link, and the insight into how much OpCache can already do.
>
> I guess preloading gets us pretty close to the tool I was imagining -
> OpCache could make assumptions that cross file boundaries, within the
> preloaded set, and could spend longer optimizing during the preloading
> phase than might be expected on a simple cache miss.
>
> I think it will be interesting to see how tools adopt that feature, and
> whether eventually we'll see autoloader functions as just a fallback
> mechanism, with most packages being enumerated in advance as large
> preloaded blocks.

What if we had a "native" autoload layer?
The native autoloader could be made to fire before userland autoloaders.
It could be based on a mapping like PSR-4, or simply a classmap.
The mappings could be defined at "compile time", or frozen early in a request.

This would allow to predict where each class is located at "compile
time" or at opcache time, allowing to do all the type checks.

An alternative would be to allow userland autoloaders to be registered
with a hash, with the promise that as long as the hash is the same,
classes remain where they are.
Or allow userland to specify "class locators" instead of autoloaders,
which could also be registered with a prediction hash.

So, the overarching idea here is to make autoloading predictable at
compile time or opcache time, and would not require an artificial
"package" concept.

As in my previous proposal, the opcache would have to store different
versions of each file, for different combinations of autoload
prediction hashes.
This would allow e.g. different applications to share some of their
PHP files without spoiling the opcache.

-- Andreas

>
> Regards,
>
> --
> Rowan Tommins (né Collins)
> [IMSoP]
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] anti-coalescing-operator

2019-10-25 Thread Andreas Hennings
On Fri, 25 Oct 2019 at 12:27, Andreas Hennings  wrote:
>
> On Fri, 25 Oct 2019 at 12:21, Rowan Tommins  wrote:
> >
> > On Thu, 24 Oct 2019 at 22:20, Kosit Supanyo  wrote:
> >
> > > But I think this functionality should be of something like pipeline
> > > operator:
> > >
> > > // send tmp variable to the next expression unconditionally
> > > $ret = $_SERVER['fname'] |>  $user->setName($$);
> > > // send tmp variable to the next expression only when $_SERVER['fname'] is
> > > set.
> > > $ret = $_SERVER['fname'] ?|>  $user->setName($$);
> > >
> > > Also the syntax like above will be consistent with the proposed
> > > safe-navigation operators.
> > >
> >
> >
> > I really like this idea, and it actually makes the pipeline operator itself
> > feel more useful, too.
> >
> > Imagine this chain, where not only might the variable not be set, but it
> > might not match a user, or the user might have no name:
> >
> > $upperCaseUserName = $_GET['user_id'] ?|> User::getById($$) ?-> getName()
> > ?|> strtoupper($$);
> >
> > As well as not needing to repeat the expression each time, as you would
> > with an "anti-coalesce", "null-safe chain" feels a clearer reading of the
> > intent here than "if not unset".
>
> I like this.
> It is a bit like a temporary local variable.
>
> One limitation is that you only have one such pipe value at a time.
> E.g. it would not cover this:
>
> return isset($a, $b) ? foo($a, $b) : null;
>
> or any nested parameters like foo(bar(baz())) where any intermediate
> value could be null.

Sorry, this last part is wrong of course. The pipe works great for
nested calls. Just with multiple parameters which all can be null it
does not.

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] anti-coalescing-operator

2019-10-25 Thread Andreas Hennings
On Fri, 25 Oct 2019 at 12:21, Rowan Tommins  wrote:
>
> On Thu, 24 Oct 2019 at 22:20, Kosit Supanyo  wrote:
>
> > But I think this functionality should be of something like pipeline
> > operator:
> >
> > // send tmp variable to the next expression unconditionally
> > $ret = $_SERVER['fname'] |>  $user->setName($$);
> > // send tmp variable to the next expression only when $_SERVER['fname'] is
> > set.
> > $ret = $_SERVER['fname'] ?|>  $user->setName($$);
> >
> > Also the syntax like above will be consistent with the proposed
> > safe-navigation operators.
> >
>
>
> I really like this idea, and it actually makes the pipeline operator itself
> feel more useful, too.
>
> Imagine this chain, where not only might the variable not be set, but it
> might not match a user, or the user might have no name:
>
> $upperCaseUserName = $_GET['user_id'] ?|> User::getById($$) ?-> getName()
> ?|> strtoupper($$);
>
> As well as not needing to repeat the expression each time, as you would
> with an "anti-coalesce", "null-safe chain" feels a clearer reading of the
> intent here than "if not unset".

I like this.
It is a bit like a temporary local variable.

One limitation is that you only have one such pipe value at a time.
E.g. it would not cover this:

return isset($a, $b) ? foo($a, $b) : null;

or any nested parameters like foo(bar(baz())) where any intermediate
value could be null.

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] anti-coalescing-operator

2019-10-24 Thread Andreas Hennings
On Thu, 24 Oct 2019 at 23:49, Ken Stanley  wrote:
>
> On Thu, Oct 24, 2019 at 4:29 PM Andreas Hennings  wrote:
>>
>> On Thu, 24 Oct 2019 at 20:59, Ken Stanley  wrote:
>> >
>> > On Thu, Oct 24, 2019 at 1:33 PM Dan Ackroyd  wrote:
>> >
>> > > On Thu, 24 Oct 2019 at 18:21, Ken Stanley  wrote:
>> > > >
>> > > > Since PHP 7.0 brought forward the Null Coalescing Operator (??), 
>> > > > writing
>> > > > more succinct code for how to handle null values has been a blessing.
>> > > But,
>> > > > what about the inverse when you want to do something when a value is 
>> > > > not
>> > > > null?
>> > >
>> > > Hi Ken,
>> > >
>> > > It may help to give a real world example, rather than a metasyntactic
>> > > one, as I can't immediately see how this would be useful.
>> > >
>> > > People have been expressing a concern over 'symbol soup' for similar
>> > > ideas. The null colalesce scenario happens frequently enough, that it
>> > > seemed to overcome the hurdle needed for acceptance. Again, giving a
>> > > real world example of what you currently need to do frequently might
>> > > help other people understand the need.
>> > >
>> > > cheers
>> > > Dan
>> > >
>> >
>> > Hi Dan,
>> >
>> > After some thought, and searching through my existing code bases, I believe
>> > I've come up with a decent code example to help demonstrate the usefulness
>> > of the proposed anti-coalescing-operator:
>> >
>> > Without !??:
>> > > >
>> > class ExampleController
>> > {
>> > /**
>> >  * PATCH a User object.
>> >  */
>> > public function saveAction(int $userId)
>> > {
>> > $user = $this->getUser($userId);
>> >
>> > if (isset($_SERVER['fname']) {
>> > $user->setName($_SERVER['fname']);
>> > }
>> >
>> > if (isset($_SERVER['lname']) {
>> > $user->setName($_SERVER['lname']);
>> > }
>> >
>> > if (isset($_SERVER['mname']) {
>> > $user->setName($_SERVER['mname']);
>> > }
>> >
>> > if (isset($_SERVER['phone']) {
>> > $user->setName($_SERVER['phone']);
>> > }
>> >
>> > if (isset($_SERVER['email']) {
>> > $user->setName($_SERVER['email']);
>> > }
>> >
>> > $this-saveUser($user);
>> > }
>> > }
>> >
>> > With !??:
>> > > >
>> > class ExampleController
>> > {
>> > /**
>> >  * PATCH a User object.
>> >  */
>> > public function saveAction(int $userId)
>> > {
>> > $user = $this->getUser($userId);
>> >
>> > $_SERVER['fname'] !?? $user->setName($_SERVER['fname']);
>> > $_SERVER['lname'] !?? $user->setName($_SERVER['lname']);
>> > $_SERVER['mname'] !?? $user->setName($_SERVER['mname']);
>> > $_SERVER['phone'] !?? $user->setName($_SERVER['phone']);
>> > $_SERVER['email'] !?? $user->setName($_SERVER['email']);
>> >
>> > $this-saveUser($user);
>> > }
>> > }
>> > Thank you,
>> > Ken Stanley
>>
>> Not convinced.
>> 1. Most of the perceived brevity is from omitting line breaks and
>> curly brackets, which is a bit misleading imo.
>
>
> This argument can be made about ?? And ?:, which have already passed muster 
> and creates a precedent.

But these were designed to produce a value, not for just control flow
- see my other point.
Yes, the same argument could have been made for those, if a similar
example had been given to advertise their introduction.

> Additionally, this is meant to compliment the existing ?? by adding a 
> negation counterpart (similar to how == has !== and > has <).
>
> I’m curious to what you find misleading about it? Its meant to literally be 
> the not-null coalescing operator.

Misleading as in "look how much shorter this is".

>
>>
>> 2. It is not the intended use of these kinds of operators (ternary or
>> null coalesce). Normally you would use them to produce a value, here
>> you use them for control flow only.
>
>
>

Re: [PHP-DEV] [RFC] anti-coalescing-operator

2019-10-24 Thread Andreas Hennings
On Fri, 25 Oct 2019 at 01:28, Sara Golemon  wrote:
>
> On Thu, Oct 24, 2019 at 12:46 PM Stephen Reay 
> wrote:
>
> > This sounds like an alternative approach (for solving the same basic
> > problem) to the nullsafe operator discussed a while back, no?
>
> https://wiki.php.net/rfc/nullsafe_calls
> >
> > At the risk of hijacking, @matthewrask asked me about ?-> a couple weeks
> ago (Javascript is making this one popular), and I threw together a rough
> PoC at
> https://github.com/php/php-src/compare/master...sgolemon:null-coalesce which
> I suspect he intends to RFC properly soon.  As long as the topic is at
> hand, what's the general appetite for it?  Should I bother polishing the
> turd?
>
> -Sara

?-> is a great idea.

The problem is this only works for method chaining, not for function
argument nesting.
So we might want something else in addition to that.

foo(bar(baz() ???) ???);

-- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] anti-coalescing-operator

2019-10-24 Thread Andreas Hennings
On Fri, 25 Oct 2019 at 01:24, Sara Golemon  wrote:
>
> On Thu, Oct 24, 2019 at 4:55 PM Ken Stanley  wrote:
>
> > > isset($_SERVER['fname']) && $user->setName($_SERVER['fname']);
> > >
> > > Will return boolean.
> > >
> >
> > Just testing the waters: Is there any appetite to have AND and OR behave
> more like Pythons operator?

Similar to javascript || and &&, although not exactly the same.

> Instead of now:
> (a or b) => bool(true) if either of a or b are true
> (a and b) =>  bool(true) is either of a or b are true
>
> Behave as:
> (a or b) => Value of a if true, b if a is not true but b is, bool(false)
> otherwise.
> (a and b) => Value of b if both are true, bool(false) otherwise.
>
> Coincidentally, this change to T_LOGICAL_AND would server OP's purpose.
>
> I'll tell ya, I'm leaning "No" because BC, but at the same time, AND/OR
> seem to be underutilized constructs and I *suspect* (having done zero
> research), that most existing uses would yield the same result as they do
> now.

At one point I started using these operators because they have
different operator precedence than || and &&, allowing assignment in
conditions without parentheses.
I gave up on this only to comply with coding standards and to not
confuse people.
I would think we will make a lot of people angry if we change this.
Imo, let's not.

Besides, a lot of this can be achieved with the ?: ternary shortcut
(not sure what's the name).

> (a or b) => Value of a if true, b if a is not true but b is, bool(false)
$a ?: ($b ?: false) === ($a ?: $b) ?: false === $a ?: $b ?: false

> (a and b) => Value of b if both are true, bool(false) otherwise.
$a ? ($b ?: false) : false === $a ? $b ?: false : false

https://3v4l.org/q8Al9

It is a bit more verbose, I admit. But in most cases we don't need to
replicate the exact behavior, and a simple ?: will do the trick.

-- Andreas

>
> -Sara

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] anti-coalescing-operator

2019-10-24 Thread Andreas Hennings
On Thu, 24 Oct 2019 at 20:59, Ken Stanley  wrote:
>
> On Thu, Oct 24, 2019 at 1:33 PM Dan Ackroyd  wrote:
>
> > On Thu, 24 Oct 2019 at 18:21, Ken Stanley  wrote:
> > >
> > > Since PHP 7.0 brought forward the Null Coalescing Operator (??), writing
> > > more succinct code for how to handle null values has been a blessing.
> > But,
> > > what about the inverse when you want to do something when a value is not
> > > null?
> >
> > Hi Ken,
> >
> > It may help to give a real world example, rather than a metasyntactic
> > one, as I can't immediately see how this would be useful.
> >
> > People have been expressing a concern over 'symbol soup' for similar
> > ideas. The null colalesce scenario happens frequently enough, that it
> > seemed to overcome the hurdle needed for acceptance. Again, giving a
> > real world example of what you currently need to do frequently might
> > help other people understand the need.
> >
> > cheers
> > Dan
> >
>
> Hi Dan,
>
> After some thought, and searching through my existing code bases, I believe
> I've come up with a decent code example to help demonstrate the usefulness
> of the proposed anti-coalescing-operator:
>
> Without !??:
> 
> class ExampleController
> {
> /**
>  * PATCH a User object.
>  */
> public function saveAction(int $userId)
> {
> $user = $this->getUser($userId);
>
> if (isset($_SERVER['fname']) {
> $user->setName($_SERVER['fname']);
> }
>
> if (isset($_SERVER['lname']) {
> $user->setName($_SERVER['lname']);
> }
>
> if (isset($_SERVER['mname']) {
> $user->setName($_SERVER['mname']);
> }
>
> if (isset($_SERVER['phone']) {
> $user->setName($_SERVER['phone']);
> }
>
> if (isset($_SERVER['email']) {
> $user->setName($_SERVER['email']);
> }
>
> $this-saveUser($user);
> }
> }
>
> With !??:
> 
> class ExampleController
> {
> /**
>  * PATCH a User object.
>  */
> public function saveAction(int $userId)
> {
> $user = $this->getUser($userId);
>
> $_SERVER['fname'] !?? $user->setName($_SERVER['fname']);
> $_SERVER['lname'] !?? $user->setName($_SERVER['lname']);
> $_SERVER['mname'] !?? $user->setName($_SERVER['mname']);
> $_SERVER['phone'] !?? $user->setName($_SERVER['phone']);
> $_SERVER['email'] !?? $user->setName($_SERVER['email']);
>
> $this-saveUser($user);
> }
> }
> Thank you,
> Ken Stanley

Not convinced.
1. Most of the perceived brevity is from omitting line breaks and
curly brackets, which is a bit misleading imo.
2. It is not the intended use of these kinds of operators (ternary or
null coalesce). Normally you would use them to produce a value, here
you use them for control flow only.
3. One purpose of the operator should be that you don't have to repeat
the variable. Here you do, e.g. $_SERVER['fname']

1.
If you would simply omit the line breaks in the first version, you
would get this:

if (isset($_SERVER['fname'])) $user->setName($_SERVER['fname']);
if (isset($_SERVER['lname'])) $user->setName($_SERVER['lname']);
if (isset($_SERVER['mname'])) $user->setName($_SERVER['mname']);
if (isset($_SERVER['phone'])) $user->setName($_SERVER['phone']);
if (isset($_SERVER['email'])) $user->setName($_SERVER['email']);

2.
Instead of "abusing" your new operator, you could simply "abuse" the
old ternary ?: instead:

!isset($_SERVER['fname']) ?: $user->setName($_SERVER['fname']);
!isset($_SERVER['lname']) ?: $user->setName($_SERVER['lname']);
!isset($_SERVER['mname']) ?: $user->setName($_SERVER['mname']);
!isset($_SERVER['phone']) ?: $user->setName($_SERVER['phone']);
!isset($_SERVER['email']) ?: $user->setName($_SERVER['email']);

3.
One way to not repeat the variable would be to introduce a temporary
local variable, like so:

if (NULL !== $fname = $_SERVER['fname'] ?? NULL) $user->setName($fname);

This gets more useful if the variable expression is something longer.

A new language feature for this purpose could have an anatomy like this:
https://3v4l.org/TjuuO or
https://3v4l.org/U6arm

and the short syntax would be like so:

   $product = ($x ??! NULL) * ($y ??! NULL);

or the NULL can be omitted:

   $product = ($x ??!) * ($y ??!);

So, the operator would break out of the current expression context,
and produce a value one level up, a bit like a try/throw/catch would,
or like a break in switch.

This is just a basic idea, it still leaves a lot of questions open.
If the expression context is multiple levels deep, how many of these
levels are we breaking?

I am not suggesting this is a good idea, but I think it is an
improvement to the original proposal.

-- Andreas

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] Reclassifying some PHP functions warning as exceptions

2019-10-21 Thread Andreas Hennings
I agree with the distinction described by Nikita.
There are definitely cases where a special return value is still the
best option.

In addition I would say in some cases it can be useful to have both
versions available: One that returns FALSE or NULL, another that
throws an exception.
This is for cases where it is up to the calling code whether a failure
is considered "exceptional" or not, or whether the calling code wants
to check the error in place or have it bubble up the call stack.

Whether something is exceptional can be a matter of expectation, and
previous knowledge within the calling code.

Take e.g. the reflection API:
- If we call "new ReflectionClass(self::class)", we assume that the
class exists. If it does not, this would warrant a runtime exception.
- If we already checked "class_exists($class)" before, then it makes
sense for "new ReflectionClass($class)" to throw an exception. We
would even want a runtime exception.
- If we receive an arbitrary string that matches a class name regex,
we would like to have a ReflectionClass::createOrNull($class), which
would NOT throw an exception, but return NULL if the class does not
exist.

If we provide two variations, they should definitely live in different
functions / methods, instead of e.g. having a parameter to determine
the failure behavior.
Having two separate methods/functions allows better code verification
by the IDE, and avoids false inspection warnings for "unhandled
exception".

Considering the BC impact, I think that providing an alternative
method/function will be the best we can do in most cases.

-- Andreas

On Mon, 21 Oct 2019 at 18:03, Rowan Tommins  wrote:
>
>  Hi David,
>
> Firstly, I agree with Nikita's general rule of which Warnings should be
> promoted and which need deeper thought.
>
> I would like to challenge one assertion in your e-mail, which is related to
> that thought process:
>
>
> On Mon, 21 Oct 2019 at 14:52, David Negrier 
> wrote:
>
> > We would be happy to promote more warnings to exceptions as those:
> > - are more predictable
> > - can be more easily caught / handled
> >
>
>
> I have always thought, and continue to think, that converting all Warnings
> (and Notices) to Exceptions is inappropriate, because Exceptions have some
> very specific behaviours, which are not always desirable:
>
> - They immediately jump control out of the current frame of execution.
> Unless you put a separate "try-catch" around every line, there is no
> "acknowledge and run next line".
> - Their default behaviour if not handled is to completely terminate
> whatever is happening, right up to showing a blank white screen.
>
> There is no general way to know whether the result of aborting execution
> completely is better or worse than carrying on with unexpected values. In a
> program that's not expecting it, either one could lead to data loss or
> corruption.
>
> There are definitely places where a Warning can reasonably be converted to
> an Exception; but I don't think this should be done as some kind of bulk
> change. Each Warning we promote should have at least one person answer the
> question "is it likely that terminating processing when this happens at
> run-time will be better than flagging a Warning and returning a defined
> value?"
>
> Regards,
> --
> Rowan Tommins
> [IMSoP]

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] Re: exit() via exception

2019-10-11 Thread Andreas Hennings
What about a new keyword in addition to "catch"?
(exact keyword would be up for discussion)

try {
}
catchExit ($exit) {

}

Or, how far would we get if we do not make this catchable, and
(perhaps) not run any "finally" block?
Could this already solve some of the technical problems, without
breaking existing expectations?

On Fri, 11 Oct 2019 at 14:32, Mark Randall  wrote:
>
> On 11/10/2019 12:05, Nikita Popov wrote:
> > This does mean though that existing code using catch(Throwable) is going to
> > catch exit()s as well. This can be avoided by introducing *yet another*
> > super-class/interface above Throwable, which is something I'd rather avoid.
>
> If it is caught, could the exit() potentially be cancelled by not
> re-throwing it?
>
> What about a hidden catch block that is inserted at compiler level?
>
> try {
>exit();
> }
> catch (\Throwable) {
>...
> }
>
> Would become:
>
> try {
>exit();
> }
> catch (\ExitThrowable $ex) {
>throw $ex;
> }
> catch (\Throwable) {
>...
> }
>
> The hidden catch block would be overwritten with a user catch block if
> one existed that caught \ExitThrowable, and it would be *required* to
> re-throw it, otherwise it would be done automatically at the end of the
> code segment or upon encountering a return etc.
>
> It's obscure though, it hides things, and I don't think I like it.
>
> Adding another level of throwable is fugly, but it's by far the most
> initiative IMO. But take the initiative and prohibit it from being used
> by itself so we don't end up in the same position a few years down the line.
>
> try {
>
> }
> catch (\TopLevelThrowableForSureThisTime $ex) {
>
> }
>
> ^^ Compiler Error.
>
> Mark Randall
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] Migration tooling and the cumulative cost of purely syntactical deprecations

2019-10-11 Thread Andreas Hennings
On Fri, 11 Oct 2019 at 12:25, Nikita Popov  wrote:
>
> Hi internals,
>
> One thing that I feel gets lost when discussion is spread over multiple
> proposals is the following fact:
>
> Purely syntactical deprecations, such as the recently discussed case of
> backticks, but also the already accepted deprecations of the "alternative
> array access syntax" and the (real) cast, can be performed automatically
> and with perfect reliability.
>
> Given a high-quality migration tool, the cost is running "php-migrate.phar
> dir/" and importantly (and this is the point I want to make) this cost is
> constant regardless of how many different things get deprecated. For this
> kind of tooling, it makes no difference whether it only has to replace the
> alternative array access syntax, or whether it also needs to replace
> backticks, some obscure variable interpolation syntax, or whatever else we
> want it to.
>
> As long as the deprecation is purely syntactical, this kind of migration
> tooling is reliable -- this certainly does not apply to all deprecations!
> For example, the also recently discussed case of undefined variables is not
> something that can be reliably migrated in an automated way (at least not
> while producing idiomatic code).
>
> Rather than starting a holy war over every single RFC that contains the
> word "deprecation", I think we should take a look at our migration story
> and possibly invest some effort into creating a "blessed" tool for this
> purpose: One that only performs migrations that are completely safe and
> should be runnable without fear of breaking code.
>
> I do realize that there is already quite a bit of existing tooling, things
> like Rector, various CS fixers and compatibility scanners, but I don't
> think that we have anything that just works out of the box to do what is
> safe and no more.
>
> If we provide this kind of tooling, I think that the cost/benefit
> calculation on deprecation changes: If we have any syntactic deprecations
> at all, then it basically doesn't matter how many different (syntactical)
> things get deprecated in one release, the migration cost is always going to
> be the same. (This is of course a slightly simplified view, but close
> enough.)

This does not really take 3rd party code into account.
(composer packages, wordpress plugins..)

What I could imagine is to let the package manager (e.g. composer) or
the package distribution portal (e.g. packagist) apply these automated
fixes to legacy packages and provide cleaned-up variations.
An "application developer" should not run clean-up scripts on 3rd
party code, unless this is automated and happens each time the 3rd
party code is updated.
And we should not assume that every 3rd party package author will take
the time to update their package.

>
> Nikita

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] exit() via exception

2019-10-11 Thread Andreas Hennings
I would think that whichever code currently calls exit() is already
expecting and accepting all the consequences, and assumes that this
won't ever be caught.

So, would it be an option to keep exit() as it is, and introduce a new
syntax construction?

Places where I have seen exit() were not really about exceptional
emergency situations, but rather a "finished" or "done".
E.g. "send headers, print ajax response, exit".
This may or may not be good practice, but it is something being done.

What other use cases exist for exit()?

> introducing *yet another* super-class/interface above Throwable, which is 
> something I'd rather avoid.

I don't see a way to avoid it ..
But if we are going to do this, we should think about whether we
really covered all cases, or if we need another layer in the future.

interface Throwable extends Catchable {}


On Fri, 11 Oct 2019 at 13:05, Nikita Popov  wrote:
>
> Hi,
>
> Currently exit() is implemented using bailout and unclean shutdown, which
> means that we're going to perform a longjmp back to the top-level scope and
> let the memory manager clean up all the memory it knows about. Anything not
> allocated using ZMM is going to leak persistently.
>
> For me, one of the most annoying things about this is that we can't perform
> proper leak checks on code using PhpUnit, because it will always exit() at
> the end, which will result in "expected" memory leaks.
>
> I think it would be good to switch exit() to work by throwing a magic
> exception, similar to what Python does. This would allow us to properly
> unwind the stack, executing finally blocks (which are currently skipped)
> and perform a clean engine shutdown.
>
> Depending on the implementation, we could also allow code to actually catch
> this exception, which may be useful for testing scenarios, as well as
> long-running daemons.
>
> I'm mainly wondering how exactly we'd go about integrating this in the
> existing exception hierarchy. Assuming that it is desirable to allow people
> to actually catch this exception, my first thought would be along these
> lines:
>
> Throwable (convert to abstract class)
> \-> Exception
> \-> Error
> \-> ExitThrowable
>
> This does mean though that existing code using catch(Throwable) is going to
> catch exit()s as well. This can be avoided by introducing *yet another*
> super-class/interface above Throwable, which is something I'd rather avoid.
>
> Anyone have thoughts on this matter?
>
> Nikita

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] Re: [VOTE] Reclassifying engine warnings

2019-10-11 Thread Andreas Hennings
On Fri, 11 Oct 2019 at 10:18, Nikita Popov  wrote:
>
> On Thu, Sep 12, 2019 at 2:17 PM Nikita Popov  wrote:
>
> > Hi internals,
> >
> > I've opened the vote on //wiki.php.net/rfc/engine_warnings.
> >
> > There are 4 votes, all of them independent. The first 3 are for specific
> > cases that were controversial during the discussion, the last one is for
> > the remainder of the proposal.
> >
> > Voting closes 2019-09-26.
> >
> > Regards,
> > Nikita
> >
>
> As people have expressed interest in hearing about direct technical
> benefits that these kinds of changes have ... let me give you an example
> that came up yesterday.
>
> Opcache performs a bunch of optimizations, and one class of optimizations
> it does are subsequent jumps on the same operand. For example:
>
> if ($x) { A; }
> if ($x) { B; }
>
> Currently, opcache will optimize the first if($x) condition to jump
> directly until after the second if($x) if the value is false, on the
> expectation that it is redundant to check the same condition twice in a
> row: The result is going to be the same. Basically the result is something
> like this:
>
> if ($x) { A; } else { goto end; }
> if ($x) { B; }
> end:
>
> Now, it turns out that this entire class of optimizations is technically
> illegal. Why? Because $x might be an undefined variable! That means that
> this optimization at the least loses an "undefined variable" notice, and at
> worse changes control flow:
>
> set_error_handler(function() {
> $GLOBALS['x'] = true;
> });
> if ($x) echo "foo\n";
> if ($x) echo "bar\n";

To be fair, the same problem would still apply for other code that
emits notices in an if condition, right?
Or does the opcache only optimize this for simple variables?

The "correct" behavior would be to analyse the code before the if(),
and only optimize if we are sure that $x will always be defined..

Otherwise, we would need to convert it to if ($x) {..} elseif
(variable_exists($x)) {goto end;}
Sadly there is currently no variable_exists() in php, and the above
code would probably lose the optimization benefit with the extra
logic.



>
> Because it's been around for years and doesn't seem to have caused any
> active issues, we're likely going to keep this, but nonetheless, it
> illustrates the kind of issue we see with these notices. Either an
> exception or nothing at all are fine, but notices caused problems.
>
> Of course there are also other problems, such as
> https://bugs.php.net/bug.php?id=78598, which is one of multiple
> use-after-free issues related to notices thrown during write operations.
> The root cause is that under the PHP memory model, it is not legal to run
> arbitrary user code while holding writable references into structures -- an
> invariant that is violated by some notices, such as the undefined array key
> one, because those notices may invoke error handlers. Again, either
> throwing nothing or throwing an exception would be unproblematic.
>
> Generally notices thrown by the engine are a pretty big pain to deal with,
> as well as something of a correctness and safety hazard. We have quite a
> few bugs in this area, though most of them are thankfully not likely to be
> hit by accident.
>
> Nikita

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



  1   2   >