On Mon, Mar 21, 2022, at 10:23 AM, Sara Golemon wrote:
> TL;DR - Yeah, PHP, but what if C++? Feel free to tell me I'm wrong and
> should feel bad. THIS IS ONLY IDLE MUSINGS.
>
> I was reading the arbitrary string interpolation thread (which I have mixed
> feelings on, but am generally okay with), and it got me thinking
> about motivations for it and other ways we might address that.
>
> I spend most of my time in C++ these days and that's going to show in this
> proposal, and the answer is probably "PHP isn't C++" and that's fine, but I
> want you to read to the end, because XSS is perennially on my mind and this
> might be one extra tool, maybe.
>
> PHP internal classes have the ability to handle operator overloads, and one
> use for overloads I quite like from C++ is streaming interfaces. Imagine
> the following:
>
> // Don't get hung up on the name, we're a long way from bikeshedding yet.
> $foo = (new \ostringstream) << "Your query returned " << $result->count()
> << " rows. The first row has ID: " >> $result->peekRow()['id'];
>
> At each << operator, the RHS is "shifted" into the string builder, and the
> object instance is returned. At the end $foo, is still that object, but
> when it's echoed or cast to string it becomes the entire combined string.
> As implementation details, we could keep the string as a list of segments
> or materialize completely, that could also be optimized to not materialize
> if we're in an output context since the intermediate complete string is
> unnecessary. Don't worry about this for now though.
>
> That by itself is... curious as an option, but not terribly interesting as
> we DO have proper interpolation and it works just fine, right?
>
> The reason I'm bothering to introduce this is that we could also build
> contextual awareness into this. During instantiation we could identify the
> context like:
>
> $forOuput = new \ostringstream\html << "You entered: " <<
> $_POST['textarea'];
> $forURIs = new \stringstream\uri << BASE_URI << '?''
> foreach ($_GET as $k => $v) {
> $forURIs << $k '=' $v << '&';
> }
>
> These specializations could perform automatic sanitization during the
> materialization phase, this could even be customizable:
>
> $custom = new \ostringstream\user( landonize(...) );
>
> We wouldn't be giving arbitrary operator overloading to the user, only
> arbitrary sanitization.
>
> Alternatively (or in addition), the point of materialization could be where
> we make this decision:
>
> echo $stream->html();
>
> ------
>
> I'd build this in userspace, but of course we don't have operator
> overloading, so the API would be a somewhat uglier function call:
>
> $stream->append("This feels ")->append(FEELING::Sad);
>
> Maybe the right answer is open the door on user-defined operator overloads,
> but my flame retardant suit is in the shop and I don't really need to open
> that mixed metaphor.
>
> -Sara
What you're proposing here is:
1. An overloadable operator on objects (via a new magic method or whatever)
that takes one argument and returns another a new instance of the same class,
with the argument included in it along with whatever the object's existing
data/context is.
2. Using that for string manipulation.
If you spell the operator >>=, then point 1 is adding a monadic bind. This has
my full support.
Using it for string manipulation is fine, although there's a bazillion other
things we can do with it that I would also very much support and can be done in
user space. Whether or not it makes sense for some of these operations to be
done in C instead is up for debate. Once an arbitrary object can have a
socket, that plus monads can push most stream operations to user space.
Building a "stream wrapper" like thing, or a filter, then becomes some mix of
object composition and binding.
$s = new StripTagsStream(new ZlibCompress(FileStream($fileName)) >>=
$htmlString;
Which... feels kinda Java clunky. It would be better if we could chain out the
wrapping levels. Which could potentially just be done in the implementation to
allow a stream on the RHS to mean that.
public function __bind(Stream|string $s) {
if ($s instanceof Stream) {
return $s->wrapAround($this);
}
// Whatever this object does with a string.
}
$s = new FileStream($fileName) >>= new ZlibCompress() >>= new StripTagsStream()
>>= $htmlString;
I'm... not sure which direction we'd want them to go in. Just spitballing.
Some way to automate that pattern would likely be good.
But yeah, a native bind operator has my support. :-)
--Larry Garfield
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php