Welcome! Log In Create A New Profile

Advanced

[PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()

Posted by Andrey Andreev 
Hi all,

I submitted a GitHub PR* to allow objects implementing __toString() to
*optionally* pass is_string() validation. More verbose wording of my
motivation can be seen in the PR description, but here are the main
points:

- Simpler way to do checks like: is_string($var) ||
method_exists($var, '__toString')
- Can be used for stricter string parameter validation in
strict_types=0 mode (otherwise any scalar type is accepted)
- Can be used for looser string parameter validation in strict_types=1
mode (__toString() objects aren't accepted there)
- Regardless of the last 2 points, it is intentionally not limited to
parameter types

* https://github.com/php/php-src/pull/2408

---

I didn't have time to write this email right after submitting the
patch, and in the meantime got some feedback from Fleshgrinder on
GitHub, which I'll quote and address here:

> Thanks for your effort and initiative.
>
> However, I strongly believe that this is the wrong approach. Adding a flag to a function directly results in the fact that the function violates the single responsibility principle. What we actually need to make this work is a "stringable" pseudo-type like the iterable type that was introduced in PHP 7.1. This "stringable" pseudo-type is the union of the scalar primitive string and any class that implements the __toString method.
>
> This has the advantage that we are actually able to use it together with strict_types, plus we have separate dedicated functions like "is_stringable" that adhere to the single responsibility principle. I actually wanted to create an RFC for that along with an implementation since iterable was accepted, but did not find the time yet.
>
> Closing note: these pseudo-types are necessary in PHP because it has no coherent type system, and there is nothing we can do about this in short term. Hence, adding such pseudo-types is the only short term solution that we actually have.

I ultimately wouldn't care if it's a separate function and did in fact
think of an is_stringable() function, but wasn't happy with the naming
- who's to say that e.g. integers aren't stringable? Bar
horribly-verbose names like
"string_or_objects_implementing__toString", I don't think there's a
way to avoid that ambiguity. :/
If we want a "stringable" type though, I guess we'll have to live with that..

I feel that debating the actual type system is way broader than I
intended this to be, so I'll refrain from going further on that for
now, as I've got some more radical ideas about it.

---

Thoughts?

Cheers,
Andrey.

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

I think this is an interesting area to explore, but do think the scope needs to be widened slightly.


On 8 March 2017 12:25:54 GMT+00:00, Andrey Andreev <[email protected]> wrote:
>I ultimately wouldn't care if it's a separate function and did in fact
>think of an is_stringable() function, but wasn't happy with the naming
>- who's to say that e.g. integers aren't stringable? Bar
>horribly-verbose names like
>"string_or_objects_implementing__toString", I don't think there's a
>way to avoid that ambiguity. :/
>If we want a "stringable" type though

Sometimes, the fact that you can't think of a good name for a function is a clue that the function isn't solving a well-defined problem.

In this case, why *wouldn't* the function return true for integers? If the question the function is asking is "can this value be cast to string?" then the answer for any integer should be "yes". If the question is "will it pass a strict type check as a string", then the answer for objects would always be "no". Am I missing a situation where casting an object would be safe, but casting an integer wouldn't?

My own thought, mentioned somewhere in the long debate about scalar type hints, was to have a "can be cast to" function, which basically predicts if a *weak* type hint would accept the value without errors or warnings. This would work for stringable objects, but also for numeric strings, etc.

Regards,

--
Rowan Collins
[IMSoP]

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On Wed, Mar 8, 2017 at 5:25 AM, Andrey Andreev <[email protected]> wrote:

> Hi all,
>
> I submitted a GitHub PR* to allow objects implementing __toString() to
> *optionally* pass is_string() validation. More verbose wording of my
> motivation can be seen in the PR description, but here are the main
> points:
>
> - Simpler way to do checks like: is_string($var) ||
> method_exists($var, '__toString')
> - Can be used for stricter string parameter validation in
> strict_types=0 mode (otherwise any scalar type is accepted)

- Can be used for looser string parameter validation in strict_types=1
> mode (__toString() objects aren't accepted there)
> - Regardless of the last 2 points, it is intentionally not limited to
> parameter types
>

If I understand correctly, you want the following to work:
declare(strict_type = 0);
function foo(string $bar) {



>
> * https://github.com/php/php-src/pull/2408
>
> ---
>
> I didn't have time to write this email right after submitting the
> patch, and in the meantime got some feedback from Fleshgrinder on
> GitHub, which I'll quote and address here:
>
> > Thanks for your effort and initiative.
> >
> > However, I strongly believe that this is the wrong approach. Adding a
> flag to a function directly results in the fact that the function violates
> the single responsibility principle. What we actually need to make this
> work is a "stringable" pseudo-type like the iterable type that was
> introduced in PHP 7.1. This "stringable" pseudo-type is the union of the
> scalar primitive string and any class that implements the __toString method.
> >
> > This has the advantage that we are actually able to use it together with
> strict_types, plus we have separate dedicated functions like
> "is_stringable" that adhere to the single responsibility principle. I
> actually wanted to create an RFC for that along with an implementation
> since iterable was accepted, but did not find the time yet.
> >
> > Closing note: these pseudo-types are necessary in PHP because it has no
> coherent type system, and there is nothing we can do about this in short
> term. Hence, adding such pseudo-types is the only short term solution that
> we actually have.
>
> I ultimately wouldn't care if it's a separate function and did in fact
> think of an is_stringable() function, but wasn't happy with the naming
> - who's to say that e.g. integers aren't stringable? Bar
> horribly-verbose names like
> "string_or_objects_implementing__toString", I don't think there's a
> way to avoid that ambiguity. :/
> If we want a "stringable" type though, I guess we'll have to live with
> that.
>
> I feel that debating the actual type system is way broader than I
> intended this to be, so I'll refrain from going further on that for
> now, as I've got some more radical ideas about it.
>
> ---
>
> Thoughts?
>
> Cheers,
> Andrey.
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>
>
On 03/08/2017 06:25 AM, Andrey Andreev wrote:
> Hi all,
>
> I submitted a GitHub PR* to allow objects implementing __toString() to
> *optionally* pass is_string() validation. More verbose wording of my
> motivation can be seen in the PR description, but here are the main
> points:
>
> - Simpler way to do checks like: is_string($var) ||
> method_exists($var, '__toString')
> - Can be used for stricter string parameter validation in
> strict_types=0 mode (otherwise any scalar type is accepted)
> - Can be used for looser string parameter validation in strict_types=1
> mode (__toString() objects aren't accepted there)
> - Regardless of the last 2 points, it is intentionally not limited to
> parameter types
>
> * https://github.com/php/php-src/pull/2408
>
> ---
>
> I didn't have time to write this email right after submitting the
> patch, and in the meantime got some feedback from Fleshgrinder on
> GitHub, which I'll quote and address here:
>
>> Thanks for your effort and initiative.
>>
>> However, I strongly believe that this is the wrong approach. Adding a flag to a function directly results in the fact that the function violates the single responsibility principle. What we actually need to make this work is a "stringable" pseudo-type like the iterable type that was introduced in PHP 7.1. This "stringable" pseudo-type is the union of the scalar primitive string and any class that implements the __toString method.
>>
>> This has the advantage that we are actually able to use it together with strict_types, plus we have separate dedicated functions like "is_stringable" that adhere to the single responsibility principle. I actually wanted to create an RFC for that along with an implementation since iterable was accepted, but did not find the time yet.
>>
>> Closing note: these pseudo-types are necessary in PHP because it has no coherent type system, and there is nothing we can do about this in short term. Hence, adding such pseudo-types is the only short term solution that we actually have.
> I ultimately wouldn't care if it's a separate function and did in fact
> think of an is_stringable() function, but wasn't happy with the naming
> - who's to say that e.g. integers aren't stringable? Bar
> horribly-verbose names like
> "string_or_objects_implementing__toString", I don't think there's a
> way to avoid that ambiguity. :/
> If we want a "stringable" type though, I guess we'll have to live with that.
>
> I feel that debating the actual type system is way broader than I
> intended this to be, so I'll refrain from going further on that for
> now, as I've got some more radical ideas about it.
>
> ---
>
> Thoughts?
>
> Cheers,
> Andrey.

I would concur with Fleshgrinder. Given the type system we have to work
with, a stringable pseudo-type that translates to is_string() (subject
to the type mode) || method_exists($var, '__toString') (not subject to
type mode) would be cleaner and more flexible than yet another flag.
Flags on method signatures are almost always a code smell.

(I suppose there's a debate to be had if an int is stringable in strict
mode; I'm not sure there myself.)

--Larry Garfield

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

On Wed, Mar 8, 2017 at 4:29 PM, Rowan Collins <[email protected]> wrote:
> Hi Andrey,
>
> I think this is an interesting area to explore, but do think the scope needs to be widened slightly.
>
>
> On 8 March 2017 12:25:54 GMT+00:00, Andrey Andreev <[email protected]> wrote:
>>I ultimately wouldn't care if it's a separate function and did in fact
>>think of an is_stringable() function, but wasn't happy with the naming
>>- who's to say that e.g. integers aren't stringable? Bar
>>horribly-verbose names like
>>"string_or_objects_implementing__toString", I don't think there's a
>>way to avoid that ambiguity. :/
>>If we want a "stringable" type though
>
> Sometimes, the fact that you can't think of a good name for a function is a clue that the function isn't solving a well-defined problem.
>
> In this case, why *wouldn't* the function return true for integers? If the question the function is asking is "can this value be cast to string?" then the answer for any integer should be "yes". If the question is "will it pass a strict type check as a string", then the answer for objects would always be "no". Am I missing a situation where casting an object would be safe, but casting an integer wouldn't?
>

Well, it may be confusing and/or a bit inconsistent since what
__toString() does is casting (and the idea will probably be ridiculed
because of this), but putting that aside - it's not about casting at
all.
The question is rather "is this value a string?", only with the added
assumption that __toString() objects are treated as "string objects"
and thus fulfill the condition (another reason why I went for an
is_string() parameter).

It's not so much about whether casting would result in loss of data,
which is what I assume you mean by "safe", but about whether the value
was ever intended to be used as a string - an object implementing
__toString() obviously is, while the same cannot be certainly said for
integers.
An integer can be a bit flag, or a key from an id => name pair, etc.

Cheers,
Andrey.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Sorry, accidently sent in the middle of typing that...

On Wed, Mar 8, 2017 at 7:42 AM, Ryan Pallas <[email protected]> wrote:

>
>
> On Wed, Mar 8, 2017 at 5:25 AM, Andrey Andreev <[email protected]> wrote:
>
>> Hi all,
>>
>> I submitted a GitHub PR* to allow objects implementing __toString() to
>> *optionally* pass is_string() validation. More verbose wording of my
>> motivation can be seen in the PR description, but here are the main
>> points:
>>
>> - Simpler way to do checks like: is_string($var) ||
>> method_exists($var, '__toString')
>> - Can be used for stricter string parameter validation in
>> strict_types=0 mode (otherwise any scalar type is accepted)
>
> - Can be used for looser string parameter validation in strict_types=1
>> mode (__toString() objects aren't accepted there)
>> - Regardless of the last 2 points, it is intentionally not limited to
>> parameter types
>>
>
> If I understand correctly, you want the following to work:
>
> declare(strict_type = 0);
function foo(string $bar) {
return $bar.'foo';
}

class Foo {
private $val;
public function __construct(string $val) {
$this->val = $val;
}
public function __toString() {
return $this->$val;
}
}

echo foo(new Foo('this is ')); // this is foo

But what happens if I change the foo function like:
function foo(string &$bar) {
$bar .= 'foo';
}

$foo = new Foo('object');
foo($foo);
var_dump($foo); // will this be an instance of Foo, or the string
"objectfoo"??

If $foo remains an object in this scope, then the function is not modifying
its value. If it becomes a string, it's an unexpected change IMO. It is
probably fine in this case, but not in the case of a more complex object.

>
>
>
>>
>> * https://github.com/php/php-src/pull/2408
>>
>> ---
>>
>> I didn't have time to write this email right after submitting the
>> patch, and in the meantime got some feedback from Fleshgrinder on
>> GitHub, which I'll quote and address here:
>>
>> > Thanks for your effort and initiative.
>> >
>> > However, I strongly believe that this is the wrong approach. Adding a
>> flag to a function directly results in the fact that the function violates
>> the single responsibility principle. What we actually need to make this
>> work is a "stringable" pseudo-type like the iterable type that was
>> introduced in PHP 7.1. This "stringable" pseudo-type is the union of the
>> scalar primitive string and any class that implements the __toString method.
>> >
>> > This has the advantage that we are actually able to use it together
>> with strict_types, plus we have separate dedicated functions like
>> "is_stringable" that adhere to the single responsibility principle. I
>> actually wanted to create an RFC for that along with an implementation
>> since iterable was accepted, but did not find the time yet.
>> >
>> > Closing note: these pseudo-types are necessary in PHP because it has no
>> coherent type system, and there is nothing we can do about this in short
>> term. Hence, adding such pseudo-types is the only short term solution that
>> we actually have.
>>
>> I ultimately wouldn't care if it's a separate function and did in fact
>> think of an is_stringable() function, but wasn't happy with the naming
>> - who's to say that e.g. integers aren't stringable? Bar
>> horribly-verbose names like
>> "string_or_objects_implementing__toString", I don't think there's a
>> way to avoid that ambiguity. :/
>> If we want a "stringable" type though, I guess we'll have to live with
>> that.
>>
>> I feel that debating the actual type system is way broader than I
>> intended this to be, so I'll refrain from going further on that for
>> now, as I've got some more radical ideas about it.
>>
>> ---
>>
>> Thoughts?
>>
>> Cheers,
>> Andrey.
>>
>> --
>> PHP Internals - PHP Runtime Development Mailing List
>> To unsubscribe, visit: http://www.php.net/unsub.php
>>
>>
>
Hi Ryan,

On Wed, Mar 8, 2017 at 5:15 PM, Ryan Pallas <[email protected]> wrote:
> Sorry, accidently sent in the middle of typing that...
>
> On Wed, Mar 8, 2017 at 7:42 AM, Ryan Pallas <[email protected]> wrote:
>>
>>
>>
>> On Wed, Mar 8, 2017 at 5:25 AM, Andrey Andreev <[email protected]> wrote:
>>>
>>> Hi all,
>>>
>>> I submitted a GitHub PR* to allow objects implementing __toString() to
>>> *optionally* pass is_string() validation. More verbose wording of my
>>> motivation can be seen in the PR description, but here are the main
>>> points:
>>>
>>> - Simpler way to do checks like: is_string($var) ||
>>> method_exists($var, '__toString')
>>> - Can be used for stricter string parameter validation in
>>> strict_types=0 mode (otherwise any scalar type is accepted)
>>>
>>> - Can be used for looser string parameter validation in strict_types=1
>>> mode (__toString() objects aren't accepted there)
>>> - Regardless of the last 2 points, it is intentionally not limited to
>>> parameter types
>>
>>
>> If I understand correctly, you want the following to work:
>>
> declare(strict_type = 0);
> function foo(string $bar) {
> return $bar.'foo';
> }
>
> class Foo {
> private $val;
> public function __construct(string $val) {
> $this->val = $val;
> }
> public function __toString() {
> return $this->$val;
> }
> }
>
> echo foo(new Foo('this is ')); // this is foo
>
> But what happens if I change the foo function like:
> function foo(string &$bar) {
> $bar .= 'foo';
> }
>
> $foo = new Foo('object');
> foo($foo);
> var_dump($foo); // will this be an instance of Foo, or the string
> "objectfoo"??
>
> If $foo remains an object in this scope, then the function is not modifying
> its value. If it becomes a string, it's an unexpected change IMO. It is
> probably fine in this case, but not in the case of a more complex object.

This already works and while the reference thing is indeed ugly, the
problems I have with it are different:

1) It also accepts every other scalar type.
2) It will not work with strict_types=1.
3) I want to do it mid-runtime, not just on function parameters.

Cheers,
Andrey.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Hey guys!

The question if an integer is a stringable is pretty much the same as
the question if an stdClass is an iterable. Sure, you can iterate an
stdClass instance with a foreach, but does it qualify as an iterable?
Definitely not, and luckily it was not implemented as such.

We can coerce an integer to a string but an integer is not a string. The
situation is different for an object that implements the __toString
method, since this object officially opted-in to the contract that it
can safely be used as a string.

In other words: in a coherent type system any object that is stringable
would extend the string class and thus would go through any string type
constraint. This is something that we cannot implement anymore at this
point because we have no coherent type system.

mixed (super type)
|
|- array is_array
|- bool is_bool
|- float is_float
|- int is_int
|- iterable is_iterable
|- null is_null
|- object is_object || instanceof
|- resource is_resource
|- closed resource get_resource_type === 'Unknown'
|- scalar is_scalar
|- string is_string
|
void (bottom type)

We have a single root node, but we directly branch into many types
without any relation to each other, a relation always requires hard
coding. Iterable is the very first of those hard coded types which was
added to the runtime to bridge a gab, namely the gab between array and
any object that implements the Traversable interface.

Stringable would be the same as Traversable---hence the -able suffix
(and I would love it if it would be possible to simply have a String
interface but that is impossible)---and bridge the gab between string
and any object that implements the Stringable interface.

Now comes the next specialty that we need to deal with: magic methods.
Any object that provides a magic __toString method automatically
implements the Stringable interface, hence, there is none. I do not like
this, and I would never do something like this if I would design a
programming language or a type system, but here we are. (Note that Go
works like this everywhere, _urgs_.)

To conclude: integers are not stringable because they are not part of
any of the two types (string + Stringable objects), just like stdClass
instances are not part of any of the two types that iterable covers.

I am open to other names, but I cannot come up with any that clearly
states its purpose: `CharBuffer`?

At this point we could also argue that any object with the magic
__toString method actually is a string. However, I believe that this
would be a crass change, which would definitely break code, e.g.:

if (is_string($v) && $v !== '') echo $v{0};

We need a pseudo-type here! We most definitely require more pseudo-types
to bridge more of our root leaves. Number comes naturally (the union of
float and int which does not coerce to any of the two), an making scalar
available to userland might be handy too. A possible pseudo-type to
cover everything that can safely be converted to a string could be str
(like in Rust). But note that safely would cover many of our types
(namely bool, float, int, null, stringable objects, resources, closed
resources, and strings). However, I definitely see use cases for all
these types to be honest (think of a message formatter).

--
Richard "Fleshgrinder" Fussenegger

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Hey! :)

The reference is actually not a problem for a Stringable because you
would get the "Only variables can be passed by reference" error with an
object if `strict_types` is enabled.

Simply because the object **MUST** be converted into a string. The
object itself does not satisfy the constraint, but the object clearly
states that it can be converted into a string at any point.

Not doing so would violate what `strict_types` actually promise us. ;)

--
Richard "Fleshgrinder" Fussenegger

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On Wed, Mar 8, 2017 at 8:51 AM, Fleshgrinder <[email protected]> wrote:

> Hey! :)
>
> The reference is actually not a problem for a Stringable because you
> would get the "Only variables can be passed by reference" error with an
> object if `strict_types` is enabled.
>
> Simply because the object **MUST** be converted into a string. The
> object itself does not satisfy the constraint, but the object clearly
> states that it can be converted into a string at any point.
>

This is the part I disagree with. The object clearly states that it can be
turned into a string when you are done using it as a object. If it gets
turned into a string, you can no longer use it as a object.

There is a difference between changing an int to string and an object to
string, in that afterwards the int->string can continue to be treated as an
int afterwards, thanks to loose typing (otherwise it wouldn't have become a
string in the first place). However with an object->string afterwards it
can ONLY be treated as a string, it can no longer be treated as an object.
Meaning

$int = 3;
foo(3);
var_dump(++$int); // 4, success, no errors

$obj = new Foo('a');
foo($obj);
var_dump($obj->method()); // Fatal error: call to member function method on
string.

To me, this doesn't make sense.

>
> Not doing so would violate what `strict_types` actually promise us. ;)
>
> --
> Richard "Fleshgrinder" Fussenegger
>
On Wed, Mar 8, 2017 at 9:50 AM, Ryan Pallas <[email protected]> wrote:

>
>
> On Wed, Mar 8, 2017 at 8:51 AM, Fleshgrinder <[email protected]> wrote:
>
>> Hey! :)
>>
>> The reference is actually not a problem for a Stringable because you
>> would get the "Only variables can be passed by reference" error with an
>> object if `strict_types` is enabled.
>>
>> Simply because the object **MUST** be converted into a string. The
>> object itself does not satisfy the constraint, but the object clearly
>> states that it can be converted into a string at any point.
>>
>
> This is the part I disagree with. The object clearly states that it can be
> turned into a string when you are done using it as a object. If it gets
> turned into a string, you can no longer use it as a object.
>
> There is a difference between changing an int to string and an object to
> string, in that afterwards the int->string can continue to be treated as an
> int afterwards, thanks to loose typing (otherwise it wouldn't have become a
> string in the first place). However with an object->string afterwards it
> can ONLY be treated as a string, it can no longer be treated as an object.
> Meaning
>
> $int = 3;
> foo(3);
>
Sorry this should have been:
foo($int);

> var_dump(++$int); // 4, success, no errors
>
> $obj = new Foo('a');
> foo($obj);
> var_dump($obj->method()); // Fatal error: call to member function method
> on string.
>
> To me, this doesn't make sense.
>
>>
>> Not doing so would violate what `strict_types` actually promise us. ;)
>>
>> --
>> Richard "Fleshgrinder" Fussenegger
>>
>
>
On 3/8/2017 5:50 PM, Ryan Pallas wrote:
> On Wed, Mar 8, 2017 at 9:50 AM, Ryan Pallas <[email protected]> wrote:
>
>>
>>
>> On Wed, Mar 8, 2017 at 8:51 AM, Fleshgrinder <[email protected]> wrote:
>>
>>> Hey! :)
>>>
>>> The reference is actually not a problem for a Stringable because you
>>> would get the "Only variables can be passed by reference" error with an
>>> object if `strict_types` is enabled.
>>>
>>> Simply because the object **MUST** be converted into a string. The
>>> object itself does not satisfy the constraint, but the object clearly
>>> states that it can be converted into a string at any point.
>>>
>>
>> This is the part I disagree with. The object clearly states that it can be
>> turned into a string when you are done using it as a object. If it gets
>> turned into a string, you can no longer use it as a object.
>>
>> There is a difference between changing an int to string and an object to
>> string, in that afterwards the int->string can continue to be treated as an
>> int afterwards, thanks to loose typing (otherwise it wouldn't have become a
>> string in the first place). However with an object->string afterwards it
>> can ONLY be treated as a string, it can no longer be treated as an object.
>> Meaning
>>
>> $int = 3;
>> foo(3);
>>
> Sorry this should have been:
> foo($int);
>
>> var_dump(++$int); // 4, success, no errors
>>
>> $obj = new Foo('a');
>> foo($obj);
>> var_dump($obj->method()); // Fatal error: call to member function method
>> on string.
>>
>> To me, this doesn't make sense.
>>
>>>
>>> Not doing so would violate what `strict_types` actually promise us. ;)
>>>
>>> --
>>> Richard "Fleshgrinder" Fussenegger
>>>
>>
>>
>

function foo(string &$s) {
$s = 'foo';
}

final class Stringable {
public function __toString() {
return 'stringable';
}
}

$stringable = new Stringable;

foo($stringable);
// Fatal error: Only variables can be passed by reference in ...

$string = (string) $stringable;
foo($string);
// Heureka!

Why is that?
At the point where we reach the call `foo($stringable)` we need to
perform various checks, which will lead to the conclusion that we
require a string and that the value of `$stringable` is an object that
can be converted to a string. That is exactly what we do. Hence, the
call is not `foo($stringable)` but rather `foo('stringable')`, the value
that we received from the conversion.

The conversion is in place, just like a cast would be, hence ...

foo((string) $stringable);

.... is equivalent to ...

foo($stringable);

.... with a type constraint of string for that particular argument.

Obviously we should yield another error message in such cases, to ensure
that people are not confused. However, this is only about the logic.

This means by implication that a ...

function foo(stringable &$s) { }

.... is impossible. Something we encounter a lot lately with many
features that we would like to have. This is also one of the reasons why
many higher languages do not support pointers in their type systems.

--
Richard "Fleshgrinder" Fussenegger

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

Andrey Andreev wrote:
> The question is rather "is this value a string?", only with the added
> assumption that __toString() objects are treated as "string objects"
> and thus fulfill the condition (another reason why I went for an
> is_string() parameter).

This is a faulty assumption. The presence of __toString() doesn't mean
the object is a string, or intended to be used like one.

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

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On 3/8/2017 7:36 PM, Andrea Faulds wrote:
> Hi,
>
> Andrey Andreev wrote:
>> The question is rather "is this value a string?", only with the added
>> assumption that __toString() objects are treated as "string objects"
>> and thus fulfill the condition (another reason why I went for an
>> is_string() parameter).
>
> This is a faulty assumption. The presence of __toString() doesn't mean
> the object is a string, or intended to be used like one.
>

What is it then in your book?

--
Richard "Fleshgrinder" Fussenegger

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Fleshgrinder wrote:
> On 3/8/2017 7:36 PM, Andrea Faulds wrote:
>> Hi,
>>
>> Andrey Andreev wrote:
>>> The question is rather "is this value a string?", only with the added
>>> assumption that __toString() objects are treated as "string objects"
>>> and thus fulfill the condition (another reason why I went for an
>>> is_string() parameter).
>>
>> This is a faulty assumption. The presence of __toString() doesn't mean
>> the object is a string, or intended to be used like one.
>>
>
> What is it then in your book?
>

It means the object can be converted to a string. But such a conversion
may entail a loss of information and not be equivalent to the object
itself. It might be a “human-readable” form, for instance.

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

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On Wed, Mar 8, 2017 at 7:44 PM, Andrea Faulds <[email protected]> wrote:

> Fleshgrinder wrote:
>
>> On 3/8/2017 7:36 PM, Andrea Faulds wrote:
>>
>>> Hi,
>>>
>>> Andrey Andreev wrote:
>>>
>>>> The question is rather "is this value a string?", only with the added
>>>> assumption that __toString() objects are treated as "string objects"
>>>> and thus fulfill the condition (another reason why I went for an
>>>> is_string() parameter).
>>>>
>>>
>>> This is a faulty assumption. The presence of __toString() doesn't mean
>>> the object is a string, or intended to be used like one.
>>>
>>>
>> What is it then in your book?
>>
>>
> It means the object can be converted to a string. But such a conversion
> may entail a loss of information and not be equivalent to the object
> itself. It might be a “human-readable” form, for instance..
>

To give an example: Exceptions implement __toString(), which contains the
exception message, location information and backtrace. Of course,
exceptions are rather different from strings and treating an exceptions as
a string is usually incorrect.

Nikita
On 3/8/2017 7:50 PM, Nikita Popov wrote:
> On Wed, Mar 8, 2017 at 7:44 PM, Andrea Faulds <[email protected]> wrote:
>
>> Fleshgrinder wrote:
>>
>>> On 3/8/2017 7:36 PM, Andrea Faulds wrote:
>>>
>>>> Hi,
>>>>
>>>> Andrey Andreev wrote:
>>>>
>>>>> The question is rather "is this value a string?", only with the added
>>>>> assumption that __toString() objects are treated as "string objects"
>>>>> and thus fulfill the condition (another reason why I went for an
>>>>> is_string() parameter).
>>>>>
>>>>
>>>> This is a faulty assumption. The presence of __toString() doesn't mean
>>>> the object is a string, or intended to be used like one.
>>>>
>>>>
>>> What is it then in your book?
>>>
>>>
>> It means the object can be converted to a string. But such a conversion
>> may entail a loss of information and not be equivalent to the object
>> itself. It might be a “human-readable” form, for instance.
>>

Sure, and? The to-be-called function does not require that all
information must be preserved, it requires that the to-be-passed
argument is convertible into a string. Most probably because it wants to
perform some actions on the string value, but the reasons are not of
interest.

As a matter of fact, any function that requests a string only asks for a
string and is not aware of its contents, hence, it is also not aware of
whether the to-be-passed argument might suffer from loss of information.

The same is true for instance if we pass a traversable of some sort to a
function that requires an iterable. We loose information! The only
difference here would be that we can use feature detection to gather
that information again, whereas in the Stringable implementation I
envision we cannot. This is because I would convert the object to a
primitive string before handing it in to the to-be-called function.

This is a controlled conversion, from the sender and receiver side which
is very handy in many situations.

That being said, it is just sugar topping, and not having it is not a
deal breaker either (since explicit casts or dedicated `toString`
methods are always there). However, it would avoid certain situations of
explicit casts where one might needlessly cast primitive strings,
whereas with the stringable data type the runtime could take care of it.
It should also be possible to generate more efficient code, since the
runtime knows better what situations are possible. Something that is
unclear with an explicit cast.

On 3/8/2017 7:50 PM, Nikita Popov wrote:
>
> To give an example: Exceptions implement __toString(), which contains the
> exception message, location information and backtrace. Of course,
> exceptions are rather different from strings and treating an exceptions as
> a string is usually incorrect.
>
> Nikita
>

For the receiver its a string and not an exception, and it does not care
that it was one.

Now, after thinking more about this, I actually think that it should
cover other scalar types too. It might be very strict, which is nice,
but at the same time this strictness does not truly add a lot of value.
After all, bool, int, float, and null can always be converted to a
string too.

In the end the use-case is to have a type constraint in strict mode that
allows more types to pass through than string only. Like a formatter
(think printf).

--
Richard "Fleshgrinder" Fussenegger

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

Please note that I had drafted this before I saw your last e-mail, so I
think some of its points are now redundant. I'm sending it anyway, in
case it helps with further thoughts.


On 08/03/2017 15:44, Fleshgrinder wrote:
> We can coerce an integer to a string but an integer is not a string. The
> situation is different for an object that implements the __toString
> method, since this object officially opted-in to the contract that it
> can safely be used as a string.

The problem is in the definition of "safely convert".

If "safely" means "reversably", i.e. the cast is non-lossy, then an
integer can be "safely" converted to a string, but (for example) an
Exception object (which hasn't ) cannot:

$a = 42;
$b = (string)$a;
$c = (int)$b;
assert($a === $c);

$a = new Exception;
$b = (string)$a;
# Cannot convert back to Exception


> To conclude: integers are not stringable because they are not part of
> any of the two types (string + Stringable objects), just like stdClass
> instances are not part of any of the two types that iterable covers.

This comes back to the same point I made to Ryan earlier: you have to
define this contract in terms of its implementation, not its purpose.
What is it that strings and Stringable objects have in common *other
than* their ability to cast to string? And since an integer can *always*
be cast to a string *and back again*, what makes it not stringable?

Going with the thought experiment of "Stringable" as an interface,
remember that interfaces implement a form of multiple inheritance. So we
don't need an ancestral relationship which links strings to ints, we can
just say that "int implements stringable". This seems perfectly
reasonable to me.


> To conclude: integers are not stringable because they are not part of
> any of the two types (string + Stringable objects), just like stdClass
> instances are not part of any of the two types that iterable covers.

I don't think this is the same case at all; the problem with "iterable"
is that stdClass should never have been usable with foreach in the first
place. The relevant comparison is not to integers, but to objects which
don't implement __toString(). Until PHP 5.2, "$foo = new stdClass; $bar
= (string)$foo;" would actually give you a string
[https://3v4l.org/XDWTh]; not a very useful one, admittedly, but by pure
logic, any object in those PHP versions was "stringable".



I think it comes down to what you're trying to achieve: the language
can't have pseudo-types for every possible combination of types, so if
you want to detect integers as one case, and other things that can be
converted to string as another, just perform your checks in the right order:

if ( is_int($foo) ) {
// ...
} elseif ( is_stringable($foo) ) {
// ...
}


Regards,

--
Rowan Collins
[IMSoP]


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

On Wed, Mar 8, 2017 at 9:39 PM, Rowan Collins <[email protected]> wrote:
>
> I think it comes down to what you're trying to achieve: the language can't
> have pseudo-types for every possible combination of types, so if you want to
> detect integers as one case, and other things that can be converted to
> string as another, just perform your checks in the right order:
>
> if ( is_int($foo) ) {
> // ...
> } elseif ( is_stringable($foo) ) {
> // ...
> }
>

This is why I was concerned about the discussion becoming too broad -
it brings us (or me anyway) back to square 1, if not even out of
bounds. I wanted the ability to do "string or string object" checks
without multiple function calls, and now the suggested solution
doesn't do that.

I do agree that we can't have pseudo-types for everything, but can we
at least have meaningful ones? How would "stringable" be different to
"string", and what's the point of either of them if they accept every
other scalar type? We're having this discussion because outside of the
so called "strict mode", PHP's string type is just as meaningful as
"scalar", which makes it useless IMO.

Cheers,
Andrey.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On 08/03/2017 22:24, Andrey Andreev wrote:
> This is why I was concerned about the discussion becoming too broad -
> it brings us (or me anyway) back to square 1, if not even out of
> bounds.

Sorry :(


> I do agree that we can't have pseudo-types for everything, but can we
> at least have meaningful ones?

Sure, but you need to justify why your particular choice of pseudo-type
is more meaningful than any of the other combinations.

I still don't understand what you're using this check for that means you
want to exclude integers. If you're passing on the value to anything
that actually needs a string, you're doing a string cast, either
explicitly or implicitly, so there's no difference between me passing
you (string)'42', (int)42, or new class { function __toString() { return
'42'; } }

My best guess was that you wanted to do something *different* if it was
an integer, but as I showed in my last example that doesn't actually
need you to exclude them from the new function.

Regards,

--
Rowan Collins
[IMSoP]


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

On Wed, Mar 8, 2017 at 8:44 PM, Andrea Faulds <[email protected]> wrote:
> Fleshgrinder wrote:
>>
>> On 3/8/2017 7:36 PM, Andrea Faulds wrote:
>>>
>>> Hi,
>>>
>>> Andrey Andreev wrote:
>>>>
>>>> The question is rather "is this value a string?", only with the added
>>>> assumption that __toString() objects are treated as "string objects"
>>>> and thus fulfill the condition (another reason why I went for an
>>>> is_string() parameter).
>>>
>>>
>>> This is a faulty assumption. The presence of __toString() doesn't mean
>>> the object is a string, or intended to be used like one.
>>>
>>
>> What is it then in your book?
>>
>
> It means the object can be converted to a string. But such a conversion may
> entail a loss of information and not be equivalent to the object itself. It
> might be a “human-readable” form, for instance.
>

There will be loss of information most of the time, indeed. That's not
the point; broadly speaking, you'd have to alias __toString() to
serialize() in order to avoid that.

If it has an explicitly declared string representation, I want to have
the option to use that easily, without writing boilerplate code to
filter-out every other type.

Cheers,
Andrey.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On 3/8/2017 11:24 PM, Andrey Andreev wrote:
> Hi again,
>
> On Wed, Mar 8, 2017 at 9:39 PM, Rowan Collins <[email protected]> wrote:
>>
>> I think it comes down to what you're trying to achieve: the language can't
>> have pseudo-types for every possible combination of types, so if you want to
>> detect integers as one case, and other things that can be converted to
>> string as another, just perform your checks in the right order:
>>
>> if ( is_int($foo) ) {
>> // ...
>> } elseif ( is_stringable($foo) ) {
>> // ...
>> }
>>
>
> This is why I was concerned about the discussion becoming too broad -
> it brings us (or me anyway) back to square 1, if not even out of
> bounds. I wanted the ability to do "string or string object" checks
> without multiple function calls, and now the suggested solution
> doesn't do that.
>
> I do agree that we can't have pseudo-types for everything, but can we
> at least have meaningful ones? How would "stringable" be different to
> "string", and what's the point of either of them if they accept every
> other scalar type? We're having this discussion because outside of the
> so called "strict mode", PHP's string type is just as meaningful as
> "scalar", which makes it useless IMO.
>
> Cheers,
> Andrey.
>

What is the use case where every other scalar (and null) type is not
acceptable? I defended that stringable should bridge only string and
objects with __toString too first, but after thinking more about it,
there is no real world reason why one would need that. Almost all use
cases I can think about evolve around strict mode and some function that
simply does not care what it was. Hence, stringable would truly act like
the into trait in Rust.

Although, to be fair, Rust does not provide Into<String> nor Into<&'a
str> for numeric types. I actually don't know why, since fmt::Display is
implemented for all of them. Then again, Rust is very, very different to
PHP.

--
Richard "Fleshgrinder" Fussenegger

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On Thu, Mar 9, 2017 at 12:42 AM, Rowan Collins <[email protected]> wrote:
>
> I still don't understand what you're using this check for that means you
> want to exclude integers. If you're passing on the value to anything that
> actually needs a string, you're doing a string cast, either explicitly or
> implicitly, so there's no difference between me passing you (string)'42',
> (int)42, or new class { function __toString() { return '42'; } }
>

This goes all the way back to the heated discussion about scalar type
hints ... Being explicit is the entire point, and why many people
wanted strict typing.

On Thu, Mar 9, 2017 at 12:43 AM, Fleshgrinder <[email protected]> wrote:
>
> What is the use case where every other scalar (and null) type is not
> acceptable? I defended that stringable should bridge only string and
> objects with __toString too first, but after thinking more about it,
> there is no real world reason why one would need that. Almost all use
> cases I can think about evolve around strict mode and some function that
> simply does not care what it was. Hence, stringable would truly act like
> the into trait in Rust.
>

Think of value objects. Perhaps you'd have a few methods on a value
object, but mostly use it to give context to a scalar type value.

For example, a Cookie object may have the cookie attributes (domain,
path, etc.) as value objects, but they can easily be created from raw
strings, while other types would be ambiguous.
A similar effect could be desirable for HTTP headers.

Also, we're talking about strings here only because we don't have
__toInteger(), __toFloat(), etc. I'm not saying we should, but if we
did - similar use cases would be present for other scalar types too.
Even easier to imagine - a DateTime object would probably have
__toInteger() returning a UNIX timestamp.

Cheers,
Andrey.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On 3/9/2017 12:32 AM, Andrey Andreev wrote:
> On Thu, Mar 9, 2017 at 12:42 AM, Rowan Collins <[email protected]> wrote:
>>
>> I still don't understand what you're using this check for that means you
>> want to exclude integers. If you're passing on the value to anything that
>> actually needs a string, you're doing a string cast, either explicitly or
>> implicitly, so there's no difference between me passing you (string)'42',
>> (int)42, or new class { function __toString() { return '42'; } }
>>
>
> This goes all the way back to the heated discussion about scalar type
> hints ... Being explicit is the entire point, and why many people
> wanted strict typing.
>

Stringable seems very explicit and strict to me, since it is opt-in.
Currently there is no way to have the ergonomics of coercion if strict
mode is active for a file. This could be a very explicit way to enable
it for portions.

On 3/9/2017 12:32 AM, Andrey Andreev wrote:
> On Thu, Mar 9, 2017 at 12:43 AM, Fleshgrinder <[email protected]> wrote:
>>
>> What is the use case where every other scalar (and null) type is not
>> acceptable? I defended that stringable should bridge only string and
>> objects with __toString too first, but after thinking more about it,
>> there is no real world reason why one would need that. Almost all use
>> cases I can think about evolve around strict mode and some function that
>> simply does not care what it was. Hence, stringable would truly act like
>> the into trait in Rust.
>>
>
> Think of value objects. Perhaps you'd have a few methods on a value
> object, but mostly use it to give context to a scalar type value.
>
> For example, a Cookie object may have the cookie attributes (domain,
> path, etc.) as value objects, but they can easily be created from raw
> strings, while other types would be ambiguous.
> A similar effect could be desirable for HTTP headers.
>

All of these can work with any other scalar value that was coerced to a
string. I actually think that most examples given will have dedicated
strictly typed methods to ensure that the value is correct for their
domain. Don't forget that a string in PHP is a binary buffer and may
contain pretty much every kind of malicious stuff that you never wanted
or expected. Validation is absolutely necessary at all times when
dealing with strings. Having an integer that is converted to a string
does not make strings more evil than they already are.

You actually already need to deal with all kinds of data that gets
coerced, since you have no control over the strict mode of the caller. ;)

On 3/9/2017 12:32 AM, Andrey Andreev wrote:
> Also, we're talking about strings here only because we don't have
> __toInteger(), __toFloat(), etc. I'm not saying we should, but if we
> did - similar use cases would be present for other scalar types too.
> Even easier to imagine - a DateTime object would probably have
> __toInteger() returning a UNIX timestamp.
>

I actually consider the existence of almost all magic methods as a
hindrance for evolving the language, and definitely do not desire to get
more of them into core. Having a `DateTime::toTimestamp()` is much more
valuable than a `DateTime::__toInteger()`.

These magic methods are a result of the incoherent type system. Treating
the symptoms instead of the root cause. The story would be different if
a `Timestamp` could extend `Integer` which would directly allow it to be
passed to all functions that are capable of dealing with an `Integer`.
(Or any other possible constellation of a more sophisticated type system.)

--
Richard "Fleshgrinder" Fussenegger

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

> I submitted a GitHub PR* to allow objects implementing __toString() to
> *optionally* pass is_string() validation. More verbose wording of my
> motivation can be seen in the PR description, but here are the main
> points:

I don't think it's right approach. is_* functions check the current type
of the value, not whether it can be converted to another type. If we
need ones that express the latter, we should have different functions.

Also, as already noted, having __toString doesn't mean it returns
something useful.

--
Stas Malyshev
smalyshev@gmail.com

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On 08/03/17 15:13, Larry Garfield wrote:
> (I suppose there's a debate to be had if an int is stringable in strict
> mode; I'm not sure there myself.)

I think the main difference between strict and what I will call normal
mode is that in the strict world of a compiler you have to call code to
provide a string of characters from a binary variable such as an
integer, while PHP was originally designed simply to provide the string
view when that is needed. Conventional user input is typed as strings,
and needs to be displayed as strings on the way back out how ever you
restrict the filtering of that data internally. Strict may provide some
users with a comfort blanket that internally they have to do fewer
checks on the data coming in but it is only creating an uncontrollable
mess instead since there are now even more ways to filter what is
essentially the same simple data.

--
Lester Caine - G8HFL
-----------------------------
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

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

On Thu, Mar 9, 2017 at 1:49 AM, Fleshgrinder <[email protected]> wrote:
> On 3/9/2017 12:32 AM, Andrey Andreev wrote:
>> On Thu, Mar 9, 2017 at 12:42 AM, Rowan Collins <[email protected]> wrote:
>>>
>>> I still don't understand what you're using this check for that means you
>>> want to exclude integers. If you're passing on the value to anything that
>>> actually needs a string, you're doing a string cast, either explicitly or
>>> implicitly, so there's no difference between me passing you (string)'42',
>>> (int)42, or new class { function __toString() { return '42'; } }
>>>
>>
>> This goes all the way back to the heated discussion about scalar type
>> hints ... Being explicit is the entire point, and why many people
>> wanted strict typing.
>>
>
> Stringable seems very explicit and strict to me, since it is opt-in.
> Currently there is no way to have the ergonomics of coercion if strict
> mode is active for a file. This could be a very explicit way to enable
> it for portions.
>

I don't understand what you're trying to say here, or rather what you
mean by "stringable" at this point ... Is it a compound type for
string and __toString() objects only, or not? Your last comments
suggest that it isn't.

> On 3/9/2017 12:32 AM, Andrey Andreev wrote:
>> On Thu, Mar 9, 2017 at 12:43 AM, Fleshgrinder <[email protected]> wrote:
>>>
>>> What is the use case where every other scalar (and null) type is not
>>> acceptable? I defended that stringable should bridge only string and
>>> objects with __toString too first, but after thinking more about it,
>>> there is no real world reason why one would need that. Almost all use
>>> cases I can think about evolve around strict mode and some function that
>>> simply does not care what it was. Hence, stringable would truly act like
>>> the into trait in Rust.
>>>
>>
>> Think of value objects. Perhaps you'd have a few methods on a value
>> object, but mostly use it to give context to a scalar type value.
>>
>> For example, a Cookie object may have the cookie attributes (domain,
>> path, etc.) as value objects, but they can easily be created from raw
>> strings, while other types would be ambiguous.
>> A similar effect could be desirable for HTTP headers.
>>
>
> All of these can work with any other scalar value that was coerced to a
> string. I actually think that most examples given will have dedicated
> strictly typed methods to ensure that the value is correct for their
> domain. Don't forget that a string in PHP is a binary buffer and may
> contain pretty much every kind of malicious stuff that you never wanted
> or expected. Validation is absolutely necessary at all times when
> dealing with strings. Having an integer that is converted to a string
> does not make strings more evil than they already are.
>

How can "any other scalar value" work? Using the cookie and headers examples:

- booleans can be used as On/Off flags for the secure and httpOnly
cookie attributes, but aren't valid literal values for any of the
attributes, or any HTTP header.
- integers are valid as a few headers' values - that is true, but
certainly in a minority of cases
- floats may be used for the q(uality) attribute in content
negotiation (and nothing else AFAIK), but that is a very, very narrow
domain
- null is obviously invalid ... who sends an empty header? And if you
have a use case where you do want to use them, we can already make
anything nullable

Of course the string values should be validated, unless you want to
allow setting arbitrary headers, e.g.:

abstract function setHeader(stringable $name, stringable $value);

.... but filtering out the known to be invalid types *is* validation.
And just that much better if it happens at compile time.

> You actually already need to deal with all kinds of data that gets
> coerced, since you have no control over the strict mode of the caller. ;)
>

Yes, and I hate that, which is why I want something that doesn't
silently accept every single scalar type.

Here's the radical idea I mentioned in my initial email: make strict
mode enforceable and/or add dedicated syntax for strict type hints.
But that goes way beyond my current proposal ... if anybody wants to
talk about this, please start a separate discussion.

> On 3/9/2017 12:32 AM, Andrey Andreev wrote:
>> Also, we're talking about strings here only because we don't have
>> __toInteger(), __toFloat(), etc. I'm not saying we should, but if we
>> did - similar use cases would be present for other scalar types too.
>> Even easier to imagine - a DateTime object would probably have
>> __toInteger() returning a UNIX timestamp.
>>
>
> I actually consider the existence of almost all magic methods as a
> hindrance for evolving the language, and definitely do not desire to get
> more of them into core. Having a `DateTime::toTimestamp()` is much more
> valuable than a `DateTime::__toInteger()`.
>
> These magic methods are a result of the incoherent type system. Treating
> the symptoms instead of the root cause. The story would be different if
> a `Timestamp` could extend `Integer` which would directly allow it to be
> passed to all functions that are capable of dealing with an `Integer`.
> (Or any other possible constellation of a more sophisticated type system.)
>

I explicitly noted that I'm NOT suggesting we should be adding more
magic, but simply using that for a hypothetical example.

Cheers,
Andrey.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On 3/9/2017 3:18 AM, Stanislav Malyshev wrote:
> Hi!
>
>> I submitted a GitHub PR* to allow objects implementing __toString() to
>> *optionally* pass is_string() validation. More verbose wording of my
>> motivation can be seen in the PR description, but here are the main
>> points:
>
> I don't think it's right approach. is_* functions check the current type
> of the value, not whether it can be converted to another type. If we
> need ones that express the latter, we should have different functions.
>
> Also, as already noted, having __toString doesn't mean it returns
> something useful.
>

This is not true at all:

1. is_dir
2. is_executable
3. is_file
4. is_finite
5. is_infinite
6. is_link
7. is_nan
8. is_readable
9. is_resource (checks the resource's type too)
10. is_uploaded_file
11. is_writable

All other `is_*` functions are either checking the type or the instance
of an object in some way.

--
Richard "Fleshgrinder" Fussenegger

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On 3/9/2017 12:47 PM, Andrey Andreev wrote:
> Hi,

Hey :)

On 3/9/2017 12:47 PM, Andrey Andreev wrote:
> On Thu, Mar 9, 2017 at 1:49 AM, Fleshgrinder <[email protected]> wrote:
>> Stringable seems very explicit and strict to me, since it is opt-in.
>> Currently there is no way to have the ergonomics of coercion if strict
>> mode is active for a file. This could be a very explicit way to enable
>> it for portions.
>>
>
> I don't understand what you're trying to say here, or rather what you
> mean by "stringable" at this point ... Is it a compound type for
> string and __toString() objects only, or not? Your last comments
> suggest that it isn't.

All scalars, null, and objects with a __toString are stringable. I
argued at first that we should only bridge string and objects with a
__toString but later came to the conclusion that it does not add value.
I understand that this might be confusing, since there are right now
many messages in this thread.

On 3/9/2017 12:47 PM, Andrey Andreev wrote:
> How can "any other scalar value" work? Using the cookie and headers examples:
>
> - booleans can be used as On/Off flags for the secure and httpOnly
> cookie attributes, but aren't valid literal values for any of the
> attributes, or any HTTP header.
> - integers are valid as a few headers' values - that is true, but
> certainly in a minority of cases
> - floats may be used for the q(uality) attribute in content
> negotiation (and nothing else AFAIK), but that is a very, very narrow
> domain
> - null is obviously invalid ... who sends an empty header? And if you
> have a use case where you do want to use them, we can already make
> anything nullable
>
> Of course the string values should be validated, unless you want to
> allow setting arbitrary headers, e.g.:
>
> abstract function setHeader(stringable $name, stringable $value);
>
> ... but filtering out the known to be invalid types *is* validation.
> And just that much better if it happens at compile time.

Because:

- bool(true) = '0'
- bool(false) = '1'
- int(n) = 'n'
- float(n) = 'n'
- null = ''
- object(s) = 's'
- string(s) = 's'

All values are possible values that I can pass to you if you use the
string type constraint. Hence, all these types are valid string values
if you request a stringable.

Stringable should work exactly like the string constraint in non-strict
mode, but regardless of the strict mode. The difference to a scalar type
constraint is that the passed values are always converted to a scalar
string, hence, the source type is unknown to the receiver.

As you can see, it does not matter if the stringable pseudo-type accepts
more than just string and objects with a __toString method.

--
Richard "Fleshgrinder" Fussenegger

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On 09/03/2017 17:37, Fleshgrinder wrote:
> On 3/9/2017 3:18 AM, Stanislav Malyshev wrote:
>
>> I don't think it's right approach. is_* functions check the current type
>> of the value, not whether it can be converted to another type. If we
>> need ones that express the latter, we should have different functions.
>>
>> Also, as already noted, having __toString doesn't mean it returns
>> something useful.
>>
> This is not true at all:
>
> 1. is_dir
> 2. is_executable
> 3. is_file
> 4. is_finite
> 5. is_infinite
> 6. is_link
> 7. is_nan
> 8. is_readable
> 9. is_resource (checks the resource's type too)
> 10. is_uploaded_file
> 11. is_writable

I think a good example is "is_callable", which evaluates not the *type*
of the value, but the *possible behaviour*. You could think of
Closure::fromCallable($foo) as "cast $foo to Closure", in which case
is_callable($foo) is "can $foo be cast to Closure?" (at least, in it's
default behaviour).

This is then much more similar to defining is_stringable($foo) to return
true for everything that can be cast to string.

Regards,

--
Rowan Collins
[IMSoP]


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Sorry, only registered users may post in this forum.

Click here to login