Showing posts with label PHP 7. Show all posts
Showing posts with label PHP 7. Show all posts

Wednesday 22 November 2017

PHP: misuse of nullable arguments

G'day:
PHP 7.1 added nullable types to the PHP language. Here's the RFC. In short, one can specify that a return type or an argument type will accept null instead of an object (or primitive) of the specified type. This code demonstrates:

function f(int $x) : int {
    return $x;
}

try {
    $x = null;
    $y = f($x);
} catch (\TypeError $e) {
    echo $e->getMessage();
}

echo PHP_EOL . PHP_EOL;

function g(?int $x) : ?int {
    return $x;
}

$x = null;
$y = g($x);
var_dump($y);

Ouptut:

Argument 1 passed to f() must be of the type integer, null given, called in [...][...] on line 8

NULL

Why do we want this? Well if we look at a statically-typed language, it makes a bit of sense:

 public class NullableTest {
     
     public static void main(String[] args){
         Integer x = null;
         Integer y = f(x);
         System.out.println(y);
     }
          
     private static Integer f(Integer x){
         return x;
     }
 }

So x is an Integer... it's just not been given a value yet, but it'll pass the type-check on the method signature. (This just outputs null, btw).

I think the merits of this functionality in PHP is limited when it comes to an argument type. If we had that equivalent code in PHP, it's not that x is an int that just happens to not have been given a value; it's a null. Not the same. It's a trivial but key difference I think.

Semantics aside there are occasional times where it's handy and - more importantly - appropriate. Consider the setMethods method of PHPUnit's MockBuilder class. The way this works is that one can pass one of three values to the method:

  • null - no methods will be mocked-out;
  • an empty array - all methods will be mocked-out;
  • an array of method names - just those methods will be mocked-out.

[docs][implementation]

Here the null has meaning, and it's distinct in meaning from an empty array. Even then this only makes "sense" because for convenience the meaning of an empty array is inversed when compared to a partial array: a partial array means "only these ones", an empty array following that logic would mean "only these ones, which is none of them", but it has been given a special meaning (and IMO "special meaning" is something one ought to be hesitant about assigning to a value). Still: it's handy.

Had I been implementing PHPUnit, I probably would not have supported null there. I'd've perhaps had two methods: mockMethods(array subsetToMock) and mockAllMethods(), and if one doesn't specify either of those, then no methods are mocked. No values with special meaning, and clearly-named methods. Shrug. PHPUnit rocks, so this is not an indictment of it.


On the other hand, most of the usage I have seen of nullable arguments is this sort of thing:

function f(?int $x = 0){
    if (is_null($x)){
        $x = 0;
    }
    // rest of function, which requires $x to be an int
}

This is a misuse of the nullable functionality. This function actually requires the argument to be an integer. Null is not a meaningful value to it. So the function simply should not accept a null. It's completely legit for a function to specify what it needs, and require the calling code to actually respect that contract.

I think in the cases I have seen this being done that the dev concerned is also working on the calling code, and they "know" the calling code could quite likely have a null for the value they need to pass to this function, but instead of dealing with that in the calling code, they push the responsibility into the method. This ain't the way to do these things. A method shouldn't have to worry about the vagaries of the situation it's being used in. It should specify what it needs from the calling code to do its job, and that's it.

Another case I've seen is that the dev concerned didn't quite get the difference between "nullable" and having an argument default. They're two different things, and they're conflating the two. When an argument is nullable, one still actually needs to pass a value to it, null or otherwise:

function f(?int $x) : int {
    return $x;
}

try {
    $y = f();
} catch (\TypeError $e) {
    echo $e->getMessage();
}

Output:

Too few arguments to function f(), 0 passed in [...][...] on line 7 and exactly 1 expected

All "nullable" means is it can be null.

I really think the occasions where a nullable argument is legit is pretty rare, so if yer writing code or reviewing someone else's code and come across one being used... stop and think about what's going on, and whether the function is taking on a responsibility the calling code oughta be looking after.

Righto.

--
Adam

Friday 2 October 2015

PHP 7: anonymous classes

G'day:
I thought it was high time I wrote an article with some code in it. Also one of my (now former ) colleagues - Cisco - asked me to write about this, so here I am.

Another new feature of PHP 7 (see the rest of my bumpf on PHP 7) is the ability to create anonymous classes. An anonymous class is to a named class what a closure (or anonymous function, or  function expression) is to a named function. Well it kinda is. Only kinda cos I think they've ballsed-up their implementation.

Here's an example of an anonymous class in action:

// inline.php

$o = new class(17) {
    private $x;

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

    function multiply($y){
        return $this->x * $y;
    }
};
$result = $o->multiply(19);

echo $result;

Here I use an anonymous class to create an object which is initialised with a property $x with a value of 17, and I then call a method to multiply that by 19. The output is predictable:

>php inline.php
323
>

This is all well and good, but an anonymous class should create a class, not an object. Obviously here it's creating the class inline, and then creating a new object out of it straight away. But what if I want to use this class again? I should be able to create just the class as a variable:

// classObject.php

$c = class {
    private $x;

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

    function multiply($y){
        return $this->x * $y;
    }
};

$o = new $c(17);
$result = $o->multiply(19);

echo $result;

This should work. Here I'm declaring the class via the anonymous class expression, and then calling new on that class. However this just errors:

>php classObject.php
PHP Parse error:  syntax error, unexpected 'class' (T_CLASS) in classObject.php on line 4

Parse error: syntax error, unexpected 'class' (T_CLASS) in classObject.php on line 4

>

I have monkeyed as much as I can with the syntax, but I cannot get an anonymous class expression to actually return a class.

Let's think about this for a second, and here's an equivalent example with a function expression to contrast:

// functionExpressionType.php

function add($x, $y){
    return $x + $y;
}
$addFunctionName = 'add';

printf('is_callable(add): %d%s', is_callable($addFunctionName), PHP_EOL);
$multiply = function($x, $y){
    return $x * $y;
};
printf('is_callable($multiply): %d%s', is_callable($multiply), PHP_EOL);

Here I look at both a function declared via a function statement, and another declared via a function expression. In both cases, they return a callable (ie: a function):

>php functionExpressionType.php
is_callable(add): 1
is_callable($multiply): 1

>

Sunday 9 August 2015

PHP 7: specifying the context for closure to use

G'day:
Here's a cool new feature of PHP 7 that doesn't seem to have been spouted off about as much as other things like speed (oh, I don't know if anyone has mentioned: did you know PHP 7 is faster than PHP 5? I'm not sure if anyone's mentioned that. In the last 5min, anyhow) and "spaceship operators". In PHP 7 one can specify the context to which a closure should bind to with a single method call.

Here's an example. The code below just calls a closure using the usual approach:

<?php
//demo.php

class A {
    private $v = "A OBJECT CONTEXT\n";
    private $closureToInject;

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

    private function injectClosure(){
        $this->closureToInject = function(){
            return $this->v;
        };
        $this->b->injected = $this->closureToInject;
    }

    function callDirectly(){
        return ($this->b->injected)();
    }
}

class B {
    private $v = "B OBJECT CONTEXT\n";
    public $injected;
}


$b = new B();
$a = new A($b);

echo $a->callDirectly();
  • The bulk of this is just getting two disconnected environments which each have their own $v property: one in the A class, and one in the B class.
  • B has a public property which we can inject a closure into if we so choose (we do).
  • In A we define a function expression (which uses closure) which simply echoes the value of the $v property.
  • And then we call said function.

Monday 3 August 2015

PHP: Has PHP7 implemented function return-type checking syntax poorly?

G'day:
I had a look at PHP 7's new (but very late tot he party) capability to type-check function return types the other day ("PHP 7: functions can now have type-checking on their return values"). The functionality kinda works OK, but I think the syntax is off:

[access modifier] [static modifier] functionName([[type] argumentName[=defaultValue]][,...])[ : return type] {
    // implementation
}

EG:


public static function getFullName(string $firstName, string $lastName) : string {
    return "$firstName $lastName";
}

The problem being that whilst all the other function modifiers are before the function name: the return type modifier is right at the end of the function signature.

This in itself is only slightly crap, but when one considers the type modifiers on arguments are before the argument name, it then just starts being a sloppy implementation.

However I wondered if there was a precedent in other popular languages I was unaware of in which this is actually something that's done, and I just hadn't noticed. So I decided to check. I've gone through what's considered the ten most popular languages and looked at what their syntax is for argument and return type checking.

Thursday 30 July 2015

PHP 7: functions can now have type-checking on their return values

G'day:
This is a kind of "part 2" to the earlier article about type-checkinng on funnction arguments: "PHP 7: new feature: enhanced type-checking on function arguments". I was goig to roll both of these into one article, but decided I already had enough material for one article with what I already had about arguments, so I pressed "send".

In PHP 5.5 (which is where my career with PHP started, adn currently is... although I use 5.6 and 7.0 at home), the way PHP has implemented type checking on functions is a bit of a incomplete mess:
  • one can specify a type on an argument;
  • but only if it is a type of object;
  • ie: not an-inbuilt type like a string or an int;
  • and whilst one can specify a type on some arguments (as per above), one cannot specify a return-type for the function as a whole at all.

I dunno who decided that approach was a good one, but I just hope they can no longer make decisions regarding how stuff is implemented in PHP in future. I imagine it was a "design by committee" sort of exercise, and there was disagreement and no one position in the "organisation" was able to go "this is bloody stupid, we're just going to do it like this [goes on to describe a sensible way of going about things]".

But we are where we are.

So now functions can have return types. The general syntax for a function with type-checking utilised seems to be

[access modifier] [static modifier] functionName([[type] argumentName[=defaultValue]][,...])[ : return type] {
    // implementation
}


Wednesday 29 July 2015

PHP 7: new feature: enhanced type-checking on function arguments

G'day:
I'm back to doing some PHP beta testing. Or more "exploration" than testing, as I'm not being very rigorous about it. PHP 7 is tup to beta 2 now (download for Windows here: "PHP 7.0 (7.0.0beta2)"). Beta 1 came and went so quickly I never even installed it, let alone looked at it. I've had too ColdFusion 12 testing to do, and messing around with JavaScript. Even at work a the moment I'm pretty much doign JavaScript day-in/day-out at present. I've probably written 100 lines of PHP code (mostly unit tests) in the last few weeks.

But, anyway, the beta waits for no person, so I figured I should look at some more stuff.

PHP 7 has expanded its type-chekcing capabilities on function arguments. In "hilarious" very typical PHP language "design" fashion, PHP has had argument type-checking on object types for some time (I cannot be bothered looking up since when), but it's never had type checking on scalar values until now. I dunno what the thinking was there. I'm sure there's an oooh so good excuse for it somewhere, but I'm equally sure that would simply start draining IQ points if a person was to read it. I shall avoid as I've already got booze for draining my IQ.

Anyway, the good side of the situation is that they've added a bit of uniformity to the language here. Scalar arguments can be type-checked now too. Kinda. Let's look at that first, as it's an easy one.

Tuesday 28 July 2015

PHP 7: PHP's error "handling" lunacy is getting on my tits

G'day:
FFS. I'm part way through an article on something completely unrelated, and needed to knock up some code to demonstrate PHP's "magic" __toString() method. That code worked fine, but my sample code cacked-out because bloody PHP still can't deal with unexpected error conditions coherently.

So here's something about __toString(), and something about PHP being f***in' stupid.

PHP's __toString() method is something one can put into a class so what if an object of that class is ever used as a string, then __toString() will be called, and if it returns a string, that string will be used. Fairly obvious stuff really. I wish CFML had this (I think Lucee kinda might? But ColdFusion does not). Anyhow, here's an example:

Sunday 28 June 2015

PHP 7: three different ways of dealing with much the same error/exception

G'day:
My foray into how PHP "handles" erroring code continues. And continues down the same vein of "how PHP has made things difficult when they really ought to be straight forward".

Friday 26 June 2015

PHP: getting PHP 5 and PHP 7 running side by side on the same machine (a better way)

G'day:
When starting to test PHP 7  I decided I did not want to sacrifice PHP 5, so wanted both running together. I horsed around getting PHP 7 running via the Apache module, and PHP 5 running via CGI. This is because Apache cannae run two different PHP modules at the same time. I documented my travails here: "Getting PHP 5.x & 7-dev running on Windows/Apache". As it turns out, there's a much easier option. Just run Apache twice...

Thursday 25 June 2015

PHP 7: new to alpha 2... Throwables now supported

G'day:
Exception handling has kinda been improved some more in PHP 7 alpha 2. Yesterday I looked at EngineExceptions ("http://blog.adamcameron.me/2015/06/php-7-php-starts-to-play-catch-up-with.html"), and part of that was a lamentation that PHP 7 had not implemented the idea of a Throwable which one could catch. Well: now they have.

PHP 7: alpha 2 breaks EngineExceptions (on purpose, as it turns out)

G'day:
I thought I was going mad... the code I wrote for yesterday's article ("PHP 7: PHP starts to play catch-up with error- / exception-handling") wasn't working today. Worried I was leading eveyrone up the garden path I revisited it, and finally worked out what the story is. This morning I "upgraded" PHP to PHP 7/alpha 2. And it broke the code.

PHP 7: null coalescing operator

G'day:
There's not much to say on this one, but I'll go through motions anyhow. First things first, I'm dead pleased they've gone for ?? rather than ?:, as I don't have to listen to people say "elvis operator" as I do in the CFML world. "Small things", I know.

Wednesday 24 June 2015

PHP 7: IIFEs supported

G'day:
I was unaware of this feature of PHP 7 until I accidentally discovered it whilst writing my previous article ("PHP 7: Three-way comparison operator (<=>)"). PHP 7 supports (Immediately-invoked function expressions).

PHP 7: Three-way comparison operator (<=>)

G'day:
Here's another new feature of PHP 7, there's not much to it, but it seems to work, and is almost sensibly implemented.

The three-way comparison operation (you can piss off if you think I'm going to call it a "spaceship operator") - <=> - is used when comparing two values. It returns <=-1, 0 or >=1 depending on whether the first operand is "less than", "equal to" or "greater than" the second operand.

For strings we already have strcmp(), but there's no expedient way of doing this sort of comparison with other data types.

Tuesday 23 June 2015

PHP 7: PHP starts to play catch-up with error- / exception-handling

G'day:
I'm not doing so well on the PHP 7 testing so far. CFML stuff keeps getting in the way.

I did start having a lot at one of the better improvements to PHP 7 the other day: improvements to exception handling. Exception handling in PHP has always been a bit "odd" (OK, I mean "shit") in my opinion. But it's slightly de-shit-ified itself in PHP 7.

Saturday 13 June 2015

PHP: getting PHP7 alpha running on Windows 8.1

G'day:
I'm mostly writing this in case anyone else has the issues I had with this. Getting PHP 7 alpha (released yesterday: "PHP 7.0.0 Alpha 1 Released") was not as straight forward as getting the dev releases of PHP 7 working. Especially for someone like me who's not really that au fait with PHP.

Wednesday 20 May 2015

Random PHP (7) bits: improvements to generators

G'day:
I was gonna have a look at output buffering in PHP today, but my attention has been hijacked by PHP 7. So instead I'm gonna have a look at some enhancements they've made to generators in 7. I'm actually not at work today, so there's a chance I'll be able to write both articles anyhow. We'll see.

Sunday 17 May 2015

Getting PHP 5.x & 7-dev running on Windows/Apache

G'day:
Yesterday evening I discovered there are runnable versions of PHP 7-dev for Windows. I'd looked around for them before, but didn't spot them. Maybe they're new. I'm really struggling to get myself as far into the PHP loop as I am in the CFML one. Admittedly mostly due to lack of really trying.

Anyhow, I downloaded it and installed it (read: unzipped it), and indeed it seemed to a) work; b) do stuff PHP 5 can't do and PHP 7 is supposed to be able to do. So I figured "yeah, OK, so I do have PHP7 running". I'll write that lot up later on, once I have a decent play with it.

All my experiments last night - which were brief as I was at the bar @ LHR awaiting a flight - were running stuff via the CLI. This is fine with PHP but not how I'm used to doing stuff. Given I'm from a CFML background I'm used to running my test code in a browser, not a prompt, so I decided to get PHP7 running on Apache today. I've just managed to get it co-habitating with PHP 5. Win.

Here's what I had to do...