rfc:autoboxing

Request for Comments: Autoboxing

Introduction

Autoboxing is a language feature that enables just-in-time conversion of a value object to another kind that is suitable to the context where it happens. In general, the source object is of a primitive data type like int or string, and the destination is an instance of a value class.

Why do we find it useful in PHP?

In PHP, unlike other scripting languages, there are fundamental differences between primitive types and classes. While they may contribute to a substantial boost of overall runtime performance, the differences prevent primitive values from having methods to operate with. For example, PHP has many functions named array_xxx() that operate on an array to produce a result in an immutable manner (i.e. don't modify the original data to store the result). They often look pretty much unintuitive when the operations are chained, because they don't allow one to write those operations in the order they occur, but to write them in a nested function calls where the innermost function gets called first. With autoboxing, you should be able to write such a chain like the following:

<?php
$sum = $arr->keys()->sum(); // with autoboxing
$sum = array_sum(array_keys($arr)); // without autoboxing
?>

Proposal

There would be a special function named __autobox() that would be called whenever primitive types are used in a context where an object should occur, and expected to return an wrapper object that represents the value passed to it if the conversion is feasible, or null if not.

<?php
function __autobox($value) {
    return ... /* some object */
}
?>

To enable autoboxing on integer values, one could write:

<?php
class IntObject {
    private $value;
 
    function __construct($value) {
        $this->value = $value;
    }
 
    function upTo($upper_bound) {
        return range($this->value, $upper_bound);
    }
}
 
function __autobox($value) {
    if (is_int($value)) {
        return new IntObject($value);
    }
    return null;
}
 
// Test code
$val = 1;
var_dump($val->upTo(10) == array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
?>

To take advantage of this autoboxing feature, I also propose a modification to the language syntax that enables arbitrary values to come to the left-hand side of arrow operators (->). With such a modification, one could write the above test code as follows.

<?php
// Test code
var_dump(1->upTo(10) == array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
?>

You may find it interesting that it would also allow you to write an arrow operator right after a new operator and parenthesized parameters.

<?php
new Foo()->bar()->baz();
?>

Discussions

Conflicts between libraries that utilizes this feature

Cornelious wrote:

I fear that a lot of people will (have) to come up with their own string libraries, int libraries etc. which will have (subtle) differences. Guess you code for two projects, one using the autbox library X and one library Y. Would you always remember which methods you're allowed to call on an int, or the order of parameters, especially when there are many methods which have a similar name?

Etienne Kneuss wrote:

It looks like a very interesting idea. However, I believe that we should learn from the __autoload experience: It does not cope well with multiple projects. I'd rather have register_autoboxer($callback) or even register_autoboxer(“type”, $callback); for instance, so that many projects could have their own autoboxer.

Nate Gordon wrote:

In general I would agree that allowing multiple instances of __auto* is a good thing, but with __autoload you are loading class names that have far more diversity than basic types. If my code relies on int being boxed with MyInt, but I use a library that wants to box it as ProjectInt we could have some very odd results that would appear to be very magical. Confining it to a namespace sounds like a potentially better solution to me than a global registry.

Possible fix

  • Per-namespace autoboxing rules (like extension methods in C#) -- The idea is to allow namespaces to have each __autobox() magic method and limiting the scope where it takes effect to the namespace in which it is declared or used through use statement.

Relation to the existing PECL libraries

Daniel Egeberg wrote:

Is there any reason why primitives couldn't be autoboxed to SplInt, SplBool, etc.1)? These classes could maybe even be extended with method aliases to the relevant functions in PHP's library.

Brian Moon wrote:

I liken this to pecl/runkit. “For all those things you.... probably shouldn't have been doing anyway”. It will create a world where scripts are not portable. And if you need that for your internal project, that is fine. But, having this as part of the PHP core would be a disaster. This is even more heinous than __autoload(), IMO. SPL fixed this for autoload. I would support an SPL extenstion to treat primitive types as SPL objects. They are standardized. Not random.

Efficiency

Dmitry Stogov wrote:

I am afraid, this magic method will make php slower even if scripts don't use this future (at least the patch disables code specialization for ZEND_INIT_METHOD_CALL) and make some future type propagation optimizations non-applicable.

Magic functions considered harmful

Dmitry Stogov wrote:

Introducing new magic function may bring a lot of troubles and open a new door for exploit writer (we already have problems with __toString() method2)).

Context information to the callback

Benjamin Eberlei wrote:

Should'nt any autobox callback should not only recieve the value to be autoboxed, but also the context information? I.e. the method name to be called on the variable? otherwise you cannot decide between different behaviours.

Exception on infeasible conversion

Pas wrote:

It could throw an exception.

Patch

A preliminary patch (may be a bit outdated) is available at http://gist.github.com/162517 .

Discussion on the List

Changelog

2010-05-04: initial version

rfc/autoboxing.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1