DEV Community

Cover image for I Want Scalar Objects in PHP
Matt Sparks
Matt Sparks

Posted on

I Want Scalar Objects in PHP

Originally Posted on DevelopmentMatt.com

Recently, I read an interesting article from Andrew Carter entitled Make PHP Great Again [cheap plug: this link was included in my most recent Newsletter]. In it Andrew brought up the topic of scalar objects. If you're not familiar with scalar objects, they represent a single value (integer, boolean, string, etc.) that you can perform operations on*. In PHP this could look something like:

$name = 'Matt';

var_dump($name->length()); // int(4)
Enter fullscreen mode Exit fullscreen mode

* It's important to note that, as far as I've been able to ascertain, the context in which you use the term "scalar" is important. Different languages implement and define scalar differently.

Soon after scalar objects were brought to the forefront of my mind, I came across the following tweet:

Liquid error: internal

To which I replied:

The Case For Scalar Objects in PHP

If you read Andrew's post he discusses how PHP's core functions are inconsistent. This topic isn't new:

PHP 8 is a great opportunity to cleanup these inconsistencies. Introducing scalar objects would provide a consistent API that would be more fluent and much more enjoyable to work with. Which of the following is more intuitive to you?

$hello = str_replace('Matt', 'Bob', 'Hello Matt!');

var_dump($hello); // Hello Bob!
Enter fullscreen mode Exit fullscreen mode

or

$hello = 'Hello Matt!';

var_dump($hello->replace('Matt', 'Bob')); // Hello Bob!
Enter fullscreen mode Exit fullscreen mode

I know this is a silly example, but doesn't the second bit of code just feel better?

Let's get scalar objects in PHP 8! Who's with me?!?

#ScalarIn8

Top comments (37)

Collapse
 
kayis profile image
K

"PHP 8 is a great opportunity to cleanup these inconsistencies."

This alone would be reason enough, lol

Collapse
 
vlasales profile image
Vlastimil Pospichal • Edited
<?php

class Name {
    private $name;

    function __construct($name) {
        $this->name = $name;
    }

    function length() {
        return mb_strlen($this->name, 'UTF-8');
    }

    /* object replace() */
    function ooReplace($needle, $replace) {
        $this->name = str_replace($needle, $replace, $this->name);
        return $this->name;
    }

    /* functional replace() */
    function replace($needle, $replace) {
        return str_replace($needle, $replace, $this->name);
    }

    function __toString() {
        return $this->name;
    }
}

$name = new Name("žluťásek");
var_dump((string) $name);                 // string(11) "žluťásek"
var_dump($name->length());                // int(8)
var_dump($name->replace('ťáse', 'ční'));  // string(10) "žlučník"
var_dump((string) $name);                 // string(11) "žluťásek"
Collapse
 
emkographics profile image
Emko

good stuff, good stuff.

Collapse
 
dallgoot profile image
dallgoot

i'd like better and consistent treatment for callables
examples of what we can not do ATM:

array_map(class::method, $array);
//
$o = new stdClass;
$o->f = function($s) {echo "Hello $s";};
$o->f("dev.to");
PHP Warning:  Uncaught Error: Call to undefined method stdClass::f() in php shell code:1
Enter fullscreen mode Exit fullscreen mode

also

  • Closure as constants
  • method toArray for SplTypes : Queue, DoublyLinkedList, etc
  • a Map datatype that is not the actual array
  • a way to programmaticaly define class constants in __construct (for int types)
  • a way to hide class properties from being listed by get_object_vars or a a method that dont list privates.

Just what's in my head ATM :)

Collapse
 
martinkordas profile image
Martin Kordas

You can call ($o->f)("dev.to") and it will work.

Collapse
 
paulthebaud profile image
Paul Thébaud

In the example you give with the callable, it would create conflict between properties and methods of classes, that are totally separated in PHP.
This will create backward compatibility problems.

Collapse
 
dallgoot profile image
dallgoot

There would be a conflict only if no policy is set up.
What seems obvious to me is to check for declared methods first.
Like you cannot "create" a class identifier that has already being declared in class.
There's already a check to prevent from using an name that is declared "private" for example.
That's the "identity" of the class.
Then if someone declares a property with same name a warning should be raised (or another error policy) because after all good practices : that doesnt' make sense to have a property named as an action or a method named as a noun.

So a simple check to put in place when invoking a class member, priority :

  • first class methods if it exists
  • then check property existence AND that is a callable

This avoids overwriting declared methods while giving the possibility to construct methods dynamically or just use callable as a valid value for a property without aliasing it prior to being able to use it.
Callable as a type must be supported in every part of the language.

Note that example show an anonymous function but it can be any object with magic "__invoke" that is supposedly a valid type for an property.
Valid type but not fully handled ATM.

Thread Thread
 
dallgoot profile image
dallgoot

and for another suggestion since i found a way to declared constant dynamically:
a way to get constants declared in a specific namespace.
something like:

// returns an array with name => value (as get_defined_constants does)
get_namespace_constants(__NAMESPACE__);
Thread Thread
 
paulthebaud profile image
Paul Thébaud

Agree with you on those points. An update can be done on this features.
And of course, it would make sense as you say, because of Closure as a type and __invoke method :)
+1 !

Collapse
 
samuraiseoul profile image
Sophie The Lionhart • Edited

Are you proposing this work similar to Java with auto boxing? I mean something like

$var = 'hella clever string here';
$var->someStringMethodHere();
Enter fullscreen mode Exit fullscreen mode

Is cool and all, but are we going to assume auto boxing here or are we going with no more primitives at all? I feel if we still want real primitives(which I assume we do), something like

$var = new String('Even more hella clever string here');
$var->someStringMethodHere();
Enter fullscreen mode Exit fullscreen mode

is a better approach than automatically auto boxing all scalars into objects. That way we have both scalar objects and primitives still.

Perhaps instead even better could be having the 'scalar object' not be a real object at all, but instead the access before in the first example I provided instead be a bit of syntactic sugar for

someStringMethodHere('hella clever string here');
Enter fullscreen mode Exit fullscreen mode

Something like that would be great. Without generics and such I don't think we really want them to be objects cause we wouldn't want something like

function foo(stdClass $obj) : void {
 echo $obj::class;
}
$var = 'scalar string';
foo($var);
Enter fullscreen mode Exit fullscreen mode

Because then there's literally no point to using stdClass as a type hint since everything would be one.

Also I just want a real god damn array. None of this map masquerading as an array bullshit. Sometimes I just need some integers one after the other in memory and not have to worry about if someone used a string as an index by accident.

Collapse
 
jsebrech profile image
Joeri Sebrechts

When using loosely typed programming languages for very large code bases you end up wishing for namespacing and strict types to enforce isolation and contracts between code modules at the language level. Once you have strict types you quickly figure out you need generics to fully type-check all your code. Once you have generics you realize you can have strictly typed generic collection classes and Maybe types (e.g. java's optional). Once you use those you realize that they'd be awesome if they had fluent API's so you could do collection->map(...)->filter(...)->sort(...)->reduce(...), and so all those collection classes and maybe types are extended to support that style (e.g. java's streams). And then when you're writing that kind of code, it would be awfully handy to have pattern matching and macro-like meta-syntax (see: scala).

And that is how all programming languages used for large projects, including PHP, are on a path to become like haskell, scala, etc. which already have all of these features. It is inevitable.

Collapse
 
pkristiancz profile image
Patrik Kristian

well, i personally dont care, php as not strictly typed language curently is not ready for this. imho.
ps: in last snippet you have syntax error. :P :)

Collapse
 
alchermd profile image
John Alcher

php as not strictly typed language curently is not ready for this.

Mind expanding your point here? What does type safety has to do with scalar objects?

Collapse
 
pkristiancz profile image
Patrik Kristian

I mean, when type is not enforced, scalar object can have different methods depending on current type. Or am I wrong?

Thread Thread
 
alchermd profile image
John Alcher

Maybe I'm missing something, but isn't how JavaScript does it with .reduce, .map, .split etc... practically what the suggested improvement wants? And JS doesn't enforce types either, right?

Thread Thread
 
drewknab profile image
Drew Knab

Yep, and it would just require an extra step sometimes to explicitly cast to the desired type. We have to do this in PHP/JS sometimes now as it is.

Thread Thread
 
pkristiancz profile image
Patrik Kristian

It could be usable tho... i imagine this:

$array = ((string) $float)->explode(.);
Collapse
 
ianfoulds profile image
Ian Foulds • Edited

But we are slowly heading in that direction, far too slowly really. Strict types help you code better and stop of lot of issues that can take time to debug.

PHP 7 made some headway here with strict_types, if you don't want it don't set it to true, or just leave out your type hints altogether - very sloppy!

Personaly I'd love to have fixed types for variables:

int $myInt = 1;
string $myString = "1234";
bool $myBool = 45;  // Fails 
int $myInt2 = "5678";  // Fails

Or even better without the annoying $ prefix which does nothing IMHO:

int myInt = 1;
string myString = "1234";
bool myBool = 45;  // Fails
int myInt2 = "5678";  // Fails
Collapse
 
tblanchard profile image
Todd Blanchard

Yes, all those Ruby and Smalltalk developers are producing completely unusable garbage because they lack manifest typing. /s

I like PHP's dynamic loose scripting nature. If you want that more verbose kind of thing, there is Java, or Swift, or whatever.

PHP's current nature fulfills a unique niche in my toolkit and I like it the way it is (although I'm all for consistent naming and moving to a more dynamic OO style).

Collapse
 
kaime profile image
Jaume Alemany

This was already proposed,implemented and rejected a few years ago:

github.com/rossriley/php-scalar-ob...

Looks like now it exists as a PHP extension:

github.com/nikic/scalar_objects

Collapse
 
ianfoulds profile image
Ian Foulds

But this would be so much better if it was part of the core and had properly published methods.

PHP needs to grow and keep up with the needs of developers rather than developers continuously getting frustrated with the lack modernity in one of the most popular languages in use. If PHP does not embrace good OOP practices it will loose it's popularity. Given that .NET Core has been available cross platform for some time now, it is becoming a popular draw for those of us who long for something better. If it were not for a host of projects and a large code base I would have moved on already.

Time will tell and for me time is running out with the arcane state PHP is still in, despite the major leap in PHP 7 it's still very old fashioned.

Collapse
 
hsemix profile image
Hamid Semix

I agree with you 100%, If it wasn't for the large code bases I have to maintain every single day, I would be using .Net Core, but hey, I think the killer feature here would be generics. That is really needed.

Collapse
 
david_j_eddy profile image
David J Eddy

The ability to set type_strict globally for a project. Either via php.ini, composer.json or version used 8.y.z-strict || 8.y.z

I feel like the more type strictness / checking PHP gets the more it is viewed as a professional language (I know, it s BS but it is what I see in around the web).

Function argument order is another one that needs correcting.

Collapse
 
alchermd profile image
John Alcher • Edited
var_dump($hello.replace('Matt', 'Bob')); // Hello Bob!

Using the dot notation only for these scalar objects would be horrible IMO. Better stick with the -> operator.

Quick edit: I realized that maybe just a typo on your part lol.

Collapse
 
mattsparks profile image
Matt Sparks

It was! I'll fix it. My bad.

Collapse
 
theodesp profile image
Theofanis Despoudis

The one and only answer to the original tweet I believe is to remove the dollar sign '$' it's hideous

Collapse
 
samuraiseoul profile image
Sophie The Lionhart

I don't hate the dollar sign per say but I agree it is pretty glaring. But I mean putting a different symbol or the word var or something in front of all the variables wouldn't be much better either. I'm curious about another idea you have for it though!

Collapse
 
ianfoulds profile image
Ian Foulds • Edited

How about prefixing with a strict type?
dev.to/mattsparks/i-want-scalar-ob...

Collapse
 
blackcat_dev profile image
Sasha Blagojevic

I’m with you!!! Um, where should I sign?

Collapse
 
phlak profile image
Chris Kankiewicz

I have been working on a library for this exact thing. Check it out: twine.phlak.net

Collapse
 
programmingdive profile image
Programming Dive • Edited

Hi Matt, don't you think that posting same content on two places will create chaos in Google search engine because this content is no longer unique on internet

Collapse
 
blackcat_dev profile image
Sasha Blagojevic

I’m with you!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.