Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 11 March 2017 13:53:10 GMT+00:00, Andrey Andreevwrote: >I don't disagree with that in general, but strictly rejecting strings >and other non-integer values would alleviate the problem for a >majority of cases; i.e. would solve the 90% problem. I guess I just don't see that as 90% at all. The interesting values to detect are the ones that are out of range, not just that somebody wrote '4' instead of 4. >What I strongly disagree on is that I should be happy with coercion, >and the almost religious resistance against (non-overridable) strict >scalar typing. For me, it's about division of responsibility: a library defines a contract, and it's up to me how I meet that contract. If I go through writing (string) everywhere, or use a pre-compiler that does that for me, I'm meeting the contract. It's no more the library's business than whether I use an IDE with dozens of templates, or hand craft my code in notepad. All coercive typing does is turn that pre-compiler on by default. What's more interesting to me is how the library can express the contract it actually wants, and scalar types by their nature make for weak constraints. All of the examples you've given are good illustrations of that, and that's why I've been trying to tease out some things that the language could do to actually help with those cases. Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On Sat, Mar 11, 2017 at 3:57 PM, Fleshgrinderwrote: > On 3/11/2017 2:53 PM, Andrey Andreev wrote: >> I don't disagree with that in general, but strictly rejecting strings >> and other non-integer values would alleviate the problem for a >> majority of cases; i.e. would solve the 90% problem. >> >> What I strongly disagree on is that I should be happy with coercion, >> and the almost religious resistance against (non-overridable) strict >> scalar typing. >> > > From whom? Where are you getting this from? We are all in favor of > adding more types to the runtime to solve more use cases. We are all > just concluding that a stringable type should not be constrained to > string + object::__toString only. All your examples just help to make it > more clear that there is no benefit in doing anything other than that. > Oh, FFS! I stopped talking about __toString() 10 emails ago, and probably the 3 of us here are all thinking about different things now. I'm out. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 3/11/2017 2:53 PM, Andrey Andreev wrote: > I don't disagree with that in general, but strictly rejecting strings > and other non-integer values would alleviate the problem for a > majority of cases; i.e. would solve the 90% problem. > > What I strongly disagree on is that I should be happy with coercion, > and the almost religious resistance against (non-overridable) strict > scalar typing. > >From whom? Where are you getting this from? We are all in favor of adding more types to the runtime to solve more use cases. We are all just concluding that a stringable type should not be constrained to string + object::__toString only. All your examples just help to make it more clear that there is no benefit in doing anything other than that. -- Richard "Fleshgrinder" Fussenegger -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On Sat, Mar 11, 2017 at 3:34 PM, Rowan Collinswrote: > On 11 March 2017 13:23:16 GMT+00:00, Andrey Andreev wrote: >>Ironically enough, the following code executes silently: >> >>$array = ['a', 'b', 'c']; >>sort($array, '2234234324'); >> >>If you don't see the problem with that, I guess it does make "no sense >>at all" from your POV. Just agree to disagree. > > > I see a problem with that, but I see exactly the same problem with this: > > $array = ['a', 'b', 'c']; > sort($array, 2234234324); > > The fact is, "int" is far too loose a type constraint to meaningfully > validate that parameter. The solution to that is not to be more strict in > rejecting strings, but to create richer types of constraint: enums, unions, > domains, etc. > I don't disagree with that in general, but strictly rejecting strings and other non-integer values would alleviate the problem for a majority of cases; i.e. would solve the 90% problem. What I strongly disagree on is that I should be happy with coercion, and the almost religious resistance against (non-overridable) strict scalar typing. Cheers, Andrey. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 11 March 2017 13:23:16 GMT+00:00, Andrey Andreevwrote: >Ironically enough, the following code executes silently: > >$array = ['a', 'b', 'c']; >sort($array, '2234234324'); > >If you don't see the problem with that, I guess it does make "no sense >at all" from your POV. Just agree to disagree. I see a problem with that, but I see exactly the same problem with this: $array = ['a', 'b', 'c']; sort($array, 2234234324); The fact is, "int" is far too loose a type constraint to meaningfully validate that parameter. The solution to that is not to be more strict in rejecting strings, but to create richer types of constraint: enums, unions, domains, etc. Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On Sat, Mar 11, 2017 at 1:39 PM, Fleshgrinderwrote: > On 3/10/2017 11:57 AM, Andrey Andreev wrote: >> Yes, they're valid string values, but the examples I gave were meant >> to show that context can make them predictably invalid, and hence why >> strict typing is desirable. > > I am totally in favor of strict types, but having a union of some type > and having the ability to constraint to it is strict. The union of > bool|int|float|null|string is stricter than the super type mixed. That's > the whole point of having a stringable. > We already have an unambiguous name for that: scalar > On 3/10/2017 4:11 PM, Andrey Andreev wrote: >> You don't, but I do care at times. > > Sorry, but your example makes no sense at all. Just because you got an > int does not even remotely mean that one of those constants was used. I'm sorry, I thought I wouldn't have to explicitly state that of course I don't know a constant was used ... "constant" is not a type. The important thing is that I know a constant was *not* used. Apparently, I am bad at examples, but take a look at sort(). Yes, even after I validate the $sort_flags values, I would never know if you passed int(1) or SORT_NUMERIC. However, knowing that no sane developer writes sort($array, 1), I can very reasonably tell you that the following is an error: $foo = '1'; sort($array, $foo); And the mere existence of SORT_FLAG_CASE makes it that much important that an error is triggered, as now my logic would be built with bitwise operations and I can't just check for one of X values. Ironically enough, the following code executes silently: $array = ['a', 'b', 'c']; sort($array, '2234234324'); If you don't see the problem with that, I guess it does make "no sense at all" from your POV. Just agree to disagree. Cheers, Andrey. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 3/10/2017 11:57 AM, Andrey Andreev wrote: > Yes, they're valid string values, but the examples I gave were meant > to show that context can make them predictably invalid, and hence why > strict typing is desirable. I am totally in favor of strict types, but having a union of some type and having the ability to constraint to it is strict. The union of bool|int|float|null|string is stricter than the super type mixed. That's the whole point of having a stringable. You can more clearly communicate what you require to do your work. Once more, it does not matter what the caller give you, you need to validate it no matter what. On 3/10/2017 4:11 PM, Andrey Andreev wrote: > You don't, but I do care at times. Sorry, but your example makes no sense at all. Just because you got an int does not even remotely mean that one of those constants was used. On top of that all, you still need to validate the int you got because it has 2^31-1 possible states, or more in case of 64bit. You need an enum in such a case, and that's the only thing that helps, nothing else. It is also inherently simple to create one, and be type safe forever. -- Richard "Fleshgrinder" Fussenegger -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
Hi! > This is not true at all: > > 1. is_dir Oh come on. I assumed I don't need to explain that the context was about is_* functions for types, not every function that starts with is_*. It doesn't even make sense to compare is_string to is_dir. -- Stas Malyshev smalys...@gmail.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 10 March 2017 15:11:39 GMT+00:00, Andrey Andreevwrote: >Let's say I asked for one of 3 class constants, that happen to hold >integer values, and you gave me a string that just happens to be >castable to one of those values - you obviously aren't using my API >correctly, but I have no way of telling you this because the compiled >hid it from both of us. I actually nearly used that same example for the opposite point: you can't *ever* know if someone used those constants, and it has nothing to do with strict types. The only way typing would help you know that is if the language had native enums, and was strict about casting to those. Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On Fri, Mar 10, 2017 at 4:20 PM, Rowan Collinswrote: > On 10 March 2017 10:57:42 GMT+00:00, Andrey Andreev wrote: >>I'm not really interested in making "strict mode" less strict - it's >>already opt-in and non-enforceable. >>I want ways to write stonger-type code in "non-strict mode", because >>the fact that "strict mode" is non-enforceable means I can never rely >>on it. > > This is a common misconception - you can absolutely rely on strict mode > enforcing your contract. > > Basically all that happens in non-strict mode is that if the caller writes: > > foo($value); > > The compiler automatically changes that to: > > foo((string)$value); > > (That's not literally how it's implemented, but it's the effect you get.) > If I want to enforce strict_types=1 operation on the caller - I can't, it can always be overriden, and thus I can never rely on it. The fact that strict_types=0 will do casting for me has no relation to this. If there's any misconception here, it is that both modes are equal - if they were, we wouldn't have 2 of them. > As the receiver of that parameter, you can't tell, and don't care, if it was > the human writing the code who added the cast, or the compiler adding it for > them. > You don't, but I do care at times. I want to know that the *caller* gave me what I want, and that the compiler didn't modify it before I received it. Why I may care about that is a different question, and nototiously hard to explain to people who don't, as all you'd always say something like "you asked for an integer and got an integer" ... Let's say I asked for one of 3 class constants, that happen to hold integer values, and you gave me a string that just happens to be castable to one of those values - you obviously aren't using my API correctly, but I have no way of telling you this because the compiled hid it from both of us. These cases may be rare and very specific, but they do exist and are valid. Yet for some reason, very few people around here want to admit that - all in the name of keeping PHP a weakly-typed language all the way. As if adding one feature would all of a sudden change your ability to write the same code you always did. Sorry about the rant, you can probably tell this irritates me a lot and I can't help it. I'll shut up now, just don't go on a mission to convince me otherwise - won't ever work. > You can't detect someone blindly writing "(string)" everywhere any more than > you can detect whether they are running in strict mode. Nor can you know if > they took it straight from unfiltered user input, or copy-pasted a literal > string to 10 different files, or a hundred other things you'd really like > them not to do. All you know is, you asked for a string, and you got one. > ... exactly - uncertainty all over the place. :) Cheers, Andrey. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 10 March 2017 10:57:42 GMT+00:00, Andrey Andreevwrote: >I'm not really interested in making "strict mode" less strict - it's >already opt-in and non-enforceable. >I want ways to write stonger-type code in "non-strict mode", because >the fact that "strict mode" is non-enforceable means I can never rely >on it. This is a common misconception - you can absolutely rely on strict mode enforcing your contract. Basically all that happens in non-strict mode is that if the caller writes: foo($value); The compiler automatically changes that to: foo((string)$value); (That's not literally how it's implemented, but it's the effect you get.) As the receiver of that parameter, you can't tell, and don't care, if it was the human writing the code who added the cast, or the compiler adding it for them. You can't detect someone blindly writing "(string)" everywhere any more than you can detect whether they are running in strict mode. Nor can you know if they took it straight from unfiltered user input, or copy-pasted a literal string to 10 different files, or a hundred other things you'd really like them not to do. All you know is, you asked for a string, and you got one. Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
Hi Rowan, On Thu, Mar 9, 2017 at 10:09 PM, Rowan Collinswrote: > On 08/03/2017 23:32, Andrey Andreev wrote: >> >> 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. > > > OK, now we have some concrete examples, thanks. I would say that in these > cases, what you actually want is a much tighter contract: not just "can be > converted to string", but "intended to be used in this context". > I know you're trying to help, but I keep reading this as "OK, now I can try to invalidate your use case". There's always an alternative solution. I only ask that our alternatives here include a middle ground between the two extremes of "accept pretty much everything" and "be as strict as possible". > It fits with what I was saying before about "if you can't name it, maybe it > isn't the right abstraction". In this case, you want to accept some > particular value objects but not, say, Exceptions - which are as useless for > your purpose as an integer, even though (string)$foo would work fine on > both. > I can name it; the name is just ridiculously long/impractical because __toString() is a special case unlike anything else in PHP. > So you want objects that have promised to behave appropriately for this > context; that could be as simple as: > > interface HTTPHeaderObject { > public function __toString(); > } > > ...with appropriate documentation that objects declaring that they implement > this interface promise to behave in a specific way when cast to string. This > is much clearer than detecting __toString(), which only promises "I can be > cast to string" - the same promise that is made by all scalar values. > My argument is that while every scalar value may promise that it "can be cast to string", the fact that you need to explicitly declare it means that only __toString() can promise that it "can be useful as a string" (context-dependent of course). > > In order to accept this or a string in a type hint, you need Union Types: > > function setHeaderValue(string|HTTPHeaderObject $value) { ... > I would love union types, but unfortunately: - https://wiki.php.net/rfc/union_types was declined - They only address parameter hints, and I also want runtime checks - Would still leave a gap for me, as I (perhaps stubbornly) want to treat __toString() objects as regular strings Though, I can live with the extra function call if we have union types as that would greatly reduce my needs related to that last point. > > For broader use, we could perhaps have named unions: > > type HTTPHeaderType = union(string, HTTPHeaderObject); > if ( $value instanceOf HTTPHeaderType ) { ... > An excellent idea, indeed - I'd be a huge proponent of that. > > Meanwhile, of course, you can just use a boring old user-defined function: > > function is_valid_http_header_value($value) { > return is_string($value) || $value instanceOf HTTPHeaderObject; > > } > Yes, and I can already wrap is_string($var) || method_exists($var, '__toString') in a function, even "override" the original is_string() with my version, in a namespace. The point was to eliminate that boilerplate code. Obviously, I'm getting zero support on the idea and we're running around in circles at this point, so I guess that's it from me here. Cheers, Andrey. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
Hi, On Thu, Mar 9, 2017 at 7:47 PM, Fleshgrinderwrote: > 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. > Yes, they're valid string values, but the examples I gave were meant to show that context can make them predictably invalid, and hence why strict typing is desirable. > 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. > I'm not really interested in making "strict mode" less strict - it's already opt-in and non-enforceable. I want ways to write stonger-type code in "non-strict mode", because the fact that "strict mode" is non-enforceable means I can never rely on it. Cheers, Andrey. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 08/03/2017 23:32, Andrey Andreev wrote: 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. OK, now we have some concrete examples, thanks. I would say that in these cases, what you actually want is a much tighter contract: not just "can be converted to string", but "intended to be used in this context". It fits with what I was saying before about "if you can't name it, maybe it isn't the right abstraction". In this case, you want to accept some particular value objects but not, say, Exceptions - which are as useless for your purpose as an integer, even though (string)$foo would work fine on both. So you want objects that have promised to behave appropriately for this context; that could be as simple as: interface HTTPHeaderObject { public function __toString(); } ...with appropriate documentation that objects declaring that they implement this interface promise to behave in a specific way when cast to string. This is much clearer than detecting __toString(), which only promises "I can be cast to string" - the same promise that is made by all scalar values. In order to accept this or a string in a type hint, you need Union Types: function setHeaderValue(string|HTTPHeaderObject $value) { ... For broader use, we could perhaps have named unions: type HTTPHeaderType = union(string, HTTPHeaderObject); if ( $value instanceOf HTTPHeaderType ) { ... Meanwhile, of course, you can just use a boring old user-defined function: function is_valid_http_header_value($value) { return is_string($value) || $value instanceOf HTTPHeaderObject; } Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 3/9/2017 8:51 PM, Rowan Collins wrote: > 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, > I also forgot about is_numeric which checks the content of a string. ;) -- Richard "Fleshgrinder" Fussenegger -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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, Fleshgrinderwrote: >> 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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
Hi, On Thu, Mar 9, 2017 at 1:49 AM, Fleshgrinderwrote: > On 3/9/2017 12:32 AM, Andrey Andreev wrote: >> On Thu, Mar 9, 2017 at 12:42 AM, Rowan Collins >> 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 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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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 smalys...@gmail.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 3/9/2017 12:32 AM, Andrey Andreev wrote: > On Thu, Mar 9, 2017 at 12:42 AM, Rowan Collins> 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 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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On Thu, Mar 9, 2017 at 12:42 AM, Rowan Collinswrote: > > 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 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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 3/8/2017 11:24 PM, Andrey Andreev wrote: > Hi again, > > On Wed, Mar 8, 2017 at 9:39 PM, Rowan Collinswrote: >> >> 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 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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
Hi again, On Wed, Mar 8, 2017 at 9:39 PM, Rowan Collinswrote: > > 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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On 3/8/2017 5:50 PM, Ryan Pallas wrote: > On Wed, Mar 8, 2017 at 9:50 AM, Ryan Pallaswrote: > >> >> >> On Wed, Mar 8, 2017 at 8:51 AM, Fleshgrinder 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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On Wed, Mar 8, 2017 at 9:50 AM, Ryan Pallaswrote: > > > On Wed, Mar 8, 2017 at 8:51 AM, Fleshgrinder 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 >> > >
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On Wed, Mar 8, 2017 at 8:51 AM, Fleshgrinderwrote: > 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 >
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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 |- intis_int |- iterable is_iterable |- null is_null |- object is_object || instanceof |- resource is_resource |- closed resourceget_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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
Hi Ryan, On Wed, Mar 8, 2017 at 5:15 PM, Ryan Pallaswrote: > Sorry, accidently sent in the middle of typing that... > > On Wed, Mar 8, 2017 at 7:42 AM, Ryan Pallas wrote: >> >> >> >> On Wed, Mar 8, 2017 at 5: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 >> >> >> 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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
Sorry, accidently sent in the middle of typing that... On Wed, Mar 8, 2017 at 7:42 AM, Ryan Pallaswrote: > > > On Wed, Mar 8, 2017 at 5: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 >> > > 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 >> >> >
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
Hi Rowan, On Wed, Mar 8, 2017 at 4:29 PM, Rowan Collinswrote: > 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 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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
On Wed, Mar 8, 2017 at 5:25 AM, Andrey Andreevwrote: > 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 > >
Re: [PHP-DEV] [Discussion] is_string(), string type and objects implementing __toString()
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 Andreevwrote: >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