[PHP-DEV] Discussion before submission of array_transpose() RFC

2022-04-23 Thread mickmackusa
I am seeking constructive feedback/advice that will give me the best
chance of a successful RFC when I eventually submit it.

Introduction:

PHP developers have had an appetite for transposing data for many
years; this is indicated by the nearly 100 Stack Overflow pages on
this narrow topic.
There are many non-native techniques being used to transpose
multidimensional data sets, but each of them are some combination of
verbose, convoluted, restrictive, and/or even flawed.
Transposing comes in handy when re-orientating data for server-side
storage or graphical/tabular display.
Ideally, PHP should have a native transposing function to put
developers out of their misery -- much like array_key_last() did.


Proposal:

Let's create an intuitive transposing function for data sets with a
minimum depth of 2 levels.
The function should also return the input parameter without complaint
when the input parameter is an empty array/object.
The previous two points are purposely stated so that this function can
accommodate fetch-all result sets from database queries (e.g.
array_transpose($stmt->fetchAll(PDO::FETCH_ASSOC));).
As a matter of flexibility, there should be no requirement that the
lone parameter be a matrix or dense data structure.
It should also preserve keys to give it a clear advantage over
pre-existing functional techniques.
The function must never receive or return a non-empty one-dimensional array.
A potential consideration is to preserve data types while transposing
-- see implementation #2 and its demo link with test data.


Background:

Here are some of the popular techniques that this new function would replace:


array_unshift($array, null);
return call_user_func_array('array_map', $array);
// outdated, unintuitive, unreliable, requires non-numeric
first-level keys, destroys associative keys
// [['single' => 'row']] becomes ['single' => 'row'] but should be
['single' => ['row']]

return array_map(null, ...$array);
// unintuitive, unreliable, requires non-numeric first-level keys,
destroys associative keys
// [['single' => 'row']] becomes ['single' => 'row'] but should be
['single' => ['row']]

$out = [];
foreach ($arr as $key => $subarr) {
foreach ($subarr as $subkey => $subvalue) {
$out[$subkey][$key] = $subvalue;
}
}
return $out;
// reliable, but verbose and not functional-style

$keys = array_keys($array);
return array_map(function($array) use ($keys) {
return array_combine($keys, $array);
}, array_map(null, ...array_values($array)));
// convoluted, unintuitive, requires input to be a matrix
// [['single' => 'row']] generates Warning: array_combine()
expects parameter 2 to be array, string given"


Considerations:

Should the function unconditionally return an array of arrays?
Should it preserve the initial data types of the first and second level?
Should data type preservation be excluded to increase the chances of
its implementation?


Rough implementations of behavior:

1. Unconditionally cast output as array of arrays.

function array_transpose(array|object $object_or_array): array|object {
$levelOneType = gettype($object_or_array);
if (!in_array($levelOneType, ['array', 'object'])) {
throw new Exception("Error: array_transpose() expects
parameter 1 to be an array or object, $levelOneType given");
}
$result = [];
foreach ($object_or_array as $rowKey => $row) {
$levelTwoType = gettype($row);
if (!in_array($levelTwoType, ['array', 'object'])) {
throw new Exception("Error: array_transpose()
expects parameter 1 to contain rows of arrays or objects,
$levelTwoType given");
}
foreach ($row as $columnKey => $value) {
$result[$columnKey][$rowKey] = $value;
}
}
return $result;
}

2. Preserve original data types of level one and level two.
https://3v4l.org/RRKlr

(If input is two dimensional, then the first occurring data type in
the second level will dictate the result's first level data type.)


function array_transpose(array|object $object_or_array): array|object {
$levelOneType = gettype($object_or_array);
if (!in_array($levelOneType, ['array', 'object'])) {
throw new Exception("Fatal error: Uncaught TypeError:
array_transpose(): Argument #1 ($object_or_array) must be of type
object|array, $levelOneType given");
}
foreach ($object_or_array as $rowKey => $row) {
$levelTwoType = gettype($row);
if (!in_array($levelTwoType, ['array', 'object'])) {
throw new Exception("Fatal error: Uncaught
TypeError: array_transpose(): Argument #1 ($object_or_array) must
contain rows of type object|array, $levelTwoType given");
}

Re: [PHP-DEV] Canonicalize "iterable" into "array|Traversable" and Reflection

2022-04-23 Thread G. P. B.
On Sat, 23 Apr 2022 at 19:19, Marco Pivetta  wrote:

> Hey George,
>
> How would the engine behave (with this RFC included) with an inheritance
> check?
>
> Specifically:
>
> interface A {
> function foo(interable $param): iterable;
> }
> interface B {
> function foo(array|\Trabersable $param): array|\Traversable:
> }
>
> Would they be compatible with each other, if put in inheritance between
> each other in any order?
>

They would, that's the main point of this change is to make handling this
sort of code easier.
As currently we need to manually check that array is indeed compatible with
iterable and similarly with Traversable.

With this change, the engine would effectively see:

interface A {
function foo(array|\Traversable $param): array|\Traversable;
}
interface B {
function foo(array|\Trabersable $param): array|\Traversable:
}

Allowing us to use the generic LSP handling (and in this case it is trivial)


Re: [PHP-DEV] Canonicalize "iterable" into "array|Traversable" and Reflection

2022-04-23 Thread Marco Pivetta
Hey George,

How would the engine behave (with this RFC included) with an inheritance
check?

Specifically:

interface A {
function foo(interable $param): iterable;
}
interface B {
function foo(array|\Trabersable $param): array|\Traversable:
}

Would they be compatible with each other, if put in inheritance between
each other in any order?

On Sat, 23 Apr 2022, 14:28 G. P. B.,  wrote:

> Hello internals,
>
> One area the engine currently needs to take special care is dealing with
> the typing relation between iterable, Traversable, and array.
> The change is to canonicalize "iterable" into "array|Traversable" as this
> then removes this special case.
>
> However, doing so breaks Reflection for iterable types and will now return
> a ReflectionUnionType instead of a ReflectionNamed type.
> There are a couple of options to proceed:
>  - Accept the BC break, and expect end users to already be handling union
> types for code running on 8.2 (least complexity)
>  - Only provide a BC layer for (?)iterable to return a ReflectionNamedType
> and have usages of iterable in a union type be exposed as Traversable|array
> (OK complexity)
>  - Full BC such that even in union types where iterabl was used
> Traversable|array gets converted back to iterable (high complexity)
>
> The PR for this change is https://github.com/php/php-src/pull/7309
>
> This PR is also blocking the implementation for DNF types, as it vastly
> simplifies the implementation of it.
>
> Best regards,
>
> George P. Banyard
>


Re: [PHP-DEV] Canonicalize "iterable" into "array|Traversable" and Reflection

2022-04-23 Thread G. P. B.
On Sat, 23 Apr 2022 at 19:06, Larry Garfield  wrote:

> On Sat, Apr 23, 2022, at 7:27 AM, G. P. B. wrote:
> > Hello internals,
> >
> > One area the engine currently needs to take special care is dealing with
> > the typing relation between iterable, Traversable, and array.
> > The change is to canonicalize "iterable" into "array|Traversable" as this
> > then removes this special case.
> >
> > However, doing so breaks Reflection for iterable types and will now
> return
> > a ReflectionUnionType instead of a ReflectionNamed type.
> > There are a couple of options to proceed:
> >  - Accept the BC break, and expect end users to already be handling union
> > types for code running on 8.2 (least complexity)
> >  - Only provide a BC layer for (?)iterable to return a
> ReflectionNamedType
> > and have usages of iterable in a union type be exposed as
> Traversable|array
> > (OK complexity)
> >  - Full BC such that even in union types where iterabl was used
> > Traversable|array gets converted back to iterable (high complexity)
> >
> > The PR for this change is https://github.com/php/php-src/pull/7309
> >
> > This PR is also blocking the implementation for DNF types, as it vastly
> > simplifies the implementation of it.
> >
> > Best regards,
> >
> > George P. Banyard
>
> To make sure I understand,
>
> Option 1:
>
> * function foo(iterable $i) // $i is ReflectionUnionType of
> array,traversable
> * function foo(iterable|Beep $i) // $i is ReflectionUnionType of
> array,Traversable,Beep
>
> Option 2:
>
> * function foo(iterable $i) // $i is ReflectionNamedType of iterable
> * function foo(iterable|Beep $i) // $i is ReflectionUnionType of
> array,Traversable,Beep
>
> Option 3:
>
> * function foo(iterable $i) // $i is ReflectionNamedType of iterable
> * function foo(iterable|Beep $i) // $i is ReflectionUnionType of
> iterable,Beep
>
> Am I following correctly?
>
> If so, I'm fine with 1 or 2.  I... cannot imagine a situation where
> someone is doing iterable|Beep to begin with, so I don't really care if
> that is a slight break.
>
> Though... I do have some code that might get cranky with Option 1, as it
> doesn't deal with union types at all (for various reasons).  I don't think
> it currently deals with iterables either, which is possibly an
> oversight...  So, I think my preference would be 2, 1, no 3.
>
> --Larry Garfield
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>
>
Correct, and a better way of visualising than I did.
I do think option 2 is the most reasonable.

Best regards,

George P. Banyard


Re: [PHP-DEV] Canonicalize "iterable" into "array|Traversable" and Reflection

2022-04-23 Thread Larry Garfield
On Sat, Apr 23, 2022, at 7:27 AM, G. P. B. wrote:
> Hello internals,
>
> One area the engine currently needs to take special care is dealing with
> the typing relation between iterable, Traversable, and array.
> The change is to canonicalize "iterable" into "array|Traversable" as this
> then removes this special case.
>
> However, doing so breaks Reflection for iterable types and will now return
> a ReflectionUnionType instead of a ReflectionNamed type.
> There are a couple of options to proceed:
>  - Accept the BC break, and expect end users to already be handling union
> types for code running on 8.2 (least complexity)
>  - Only provide a BC layer for (?)iterable to return a ReflectionNamedType
> and have usages of iterable in a union type be exposed as Traversable|array
> (OK complexity)
>  - Full BC such that even in union types where iterabl was used
> Traversable|array gets converted back to iterable (high complexity)
>
> The PR for this change is https://github.com/php/php-src/pull/7309
>
> This PR is also blocking the implementation for DNF types, as it vastly
> simplifies the implementation of it.
>
> Best regards,
>
> George P. Banyard

To make sure I understand, 

Option 1:

* function foo(iterable $i) // $i is ReflectionUnionType of array,traversable
* function foo(iterable|Beep $i) // $i is ReflectionUnionType of 
array,Traversable,Beep

Option 2:

* function foo(iterable $i) // $i is ReflectionNamedType of iterable
* function foo(iterable|Beep $i) // $i is ReflectionUnionType of 
array,Traversable,Beep

Option 3:

* function foo(iterable $i) // $i is ReflectionNamedType of iterable
* function foo(iterable|Beep $i) // $i is ReflectionUnionType of iterable,Beep

Am I following correctly?

If so, I'm fine with 1 or 2.  I... cannot imagine a situation where someone is 
doing iterable|Beep to begin with, so I don't really care if that is a slight 
break.

Though... I do have some code that might get cranky with Option 1, as it 
doesn't deal with union types at all (for various reasons).  I don't think it 
currently deals with iterables either, which is possibly an oversight...  So, I 
think my preference would be 2, 1, no 3.

--Larry Garfield

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



[PHP-DEV] Canonicalize "iterable" into "array|Traversable" and Reflection

2022-04-23 Thread G. P. B.
Hello internals,

One area the engine currently needs to take special care is dealing with
the typing relation between iterable, Traversable, and array.
The change is to canonicalize "iterable" into "array|Traversable" as this
then removes this special case.

However, doing so breaks Reflection for iterable types and will now return
a ReflectionUnionType instead of a ReflectionNamed type.
There are a couple of options to proceed:
 - Accept the BC break, and expect end users to already be handling union
types for code running on 8.2 (least complexity)
 - Only provide a BC layer for (?)iterable to return a ReflectionNamedType
and have usages of iterable in a union type be exposed as Traversable|array
(OK complexity)
 - Full BC such that even in union types where iterabl was used
Traversable|array gets converted back to iterable (high complexity)

The PR for this change is https://github.com/php/php-src/pull/7309

This PR is also blocking the implementation for DNF types, as it vastly
simplifies the implementation of it.

Best regards,

George P. Banyard


[PHP-DEV] Re: LOCK_SH for file_get_contents ?

2022-04-23 Thread Hans Henrik Bergan
can we at least change FILE_USE_INCLUDE_PATH to 8 / (1<<3)  ? that would
allow a userland implementation of file_get_contents to support LOCK_SH and
FILE_USE_INCLUDE_PATH

The current situation is so bad that FILE_USE_INCLUDE_PATH literally cannot
be used in strict_mode=1, it's pretty much completely useless since php
7.0, so I doubt it'll break anything keeping up with new releases of PHP.

On Mon, 13 Dec 2021 at 13:57, Hans Henrik Bergan 
wrote:

> This has been requested for years (since at least 2009?) but it seems no
> actual plan has been proposed
> How about this?
> since we already have the constant FILE_USE_INCLUDE_PATH , seems it was
> introduced in PHP5.0.0,
>
> 1: FILE_USE_INCLUDE_PATH currently collides with LOCK_SH (they're both 1),
> lets change FILE_USE_INCLUDE_PATH to something that doesn't collide with
> any of LOCK_SH | LOCK_EX | LOCK_NB
> for example  (1 << 3) / int(8)
> 2: change argument #2 from "bool $use_include_path = false" to "int|bool
> $flags = 0" , treating bool(false) the same as int(0) and bool(true) the
> same as FILE_USE_INCLUDE_PATH , and support LOCK_SH | LOCK_NB |
> FILE_USE_INCLUDE_PATH here
> (i think LOCK_EX could also be supported, but that might be controversial,
> and the use case is certainly limited, if there is one at all)
>
> because it's kind of silly, and at times annoying, that file_put_contents
> support LOCK_EX but file_get_contents does not support LOCK_SH
>