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

Reply via email to