How new object working in the latest version of PHP - php

As per the PHP 7.2 documentation
A new type, object, has been introduced that can be used for (contravariant) parameter typing and (covariant) return typing of any objects.
And the following example has been given
<?php
function test(object $obj) : object
{
return new SplQueue();
}
test(new StdClass());
Can someone elaborate what is meant by contravariant parameter and covariant return type and how this new object work

object in both places in your code can return generic object i.e an instance of any type. (as shown in your example)
Else it would need to be:
<?php
function test(StdClass $obj) : SplQueue
{
return new SplQueue();
}
test(new StdClass());

Related

Encoding clone $this in JsonSerializable

This simplified case is resulting in a PHP segfault (exit 127):
class Datum implements \JsonSerializable{
public function jsonSerialize(){
return clone $this;
}
}
echo json_encode(new Datum);
The last line of code results in exit(127). I'm unable to retrieve any stack in my current environment.
Meanwhile, removing the clone token works.
Is there any possible explanation why this is happening?
This code results in an infinite recursion.
It appears that the PHP JSON module supports JsonSerializable in this manner (pseudocode):
function json_encode($data){
if($data instanceof JsonSerializable) return json_encode($data->jsonSerialize());
else real_json_encode($data); // handling primitive data or arrays or pure data objects
}
If you return yet another instance of JsonSerializable, json_encode is going to try to serialize it again, resulting in an infinite recursion.
This is working for return $this;, however, probably due to intentional workaround from json_encode's implementation where it goes straight to real json_encode when the returned object is identical, i.e. when $this is returned. However this is not happening for cloned objects since $a !== clone $a.
References
This answer can be supported by reference from the php-src.
// in php_json_encode_zval
if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) {
return php_json_encode_serializable_object(buf, val, options, encoder);
}
// in php_json_encode_serializable_object
if ((Z_TYPE(retval) == IS_OBJECT) &&
(Z_OBJ(retval) == Z_OBJ_P(val))) {
/* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
return_code = php_json_encode_array(buf, &retval, options, encoder);
} else {
/* All other types, encode as normal */
return_code = php_json_encode_zval(buf, &retval, options, encoder);
PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
}
These snippets prove that PHP would encode return $this; as an array (or as a non-serializable object), while returning anything else makes Z_OBJ(retval) == Z_OBJ_P(val) false, going to the else block which recursively calls php_json_encode_zval again.
TL;DR, Simple solution: return (array) $this; instead of clone $this;.

Set a referenced variable to a newly initialized class

I have a method, which takes a reference
// CarService.php
public function getCars(&$carCollection = null)
{
$promise = // guzzle request for getting all cars would be here
$promise->then(function (ResponseInterface $response) use (&$carCollection) {
$cars= json_decode($response->getBody(), true);
$carCollection= new CarCollection($cars);
});
}
However, when accessing the collection and trying to reuse it, I'm getting the error
Argument 1 passed to {placeholder} must be an instance of {placeholder}, null given
I know that the reason for this is, that the constructor returns nothing, but how can I still assign my variable to a new instance of the CarCollection (which extends Doctrine's ArrayCollection)
I even tried it with a static method as a work around
// CarCollection.php
public static function create(array $cars): CarCollection
{
$carCollection = new CarCollection($cars);
return $carCollection;
}
// CarService.php
public function getCars(&$carCollection = null)
{
$cars = // curl request for getting all cars would be here
$carCollection = CarCollection::create($cars)
}
but it's still null. Why is that? How can I set a referenced variable to a new class?
I access the method like this
$carService = $this->get('tzfrs.vehicle.services.car');
$carCollection = null;
$promises = [
$carService->getCars($carCollection)
];
\GuzzleHttp\Promise\unwrap($promises);
var_dump($carCollection); // null
When I set the reference directly, eg.
// CarService.php
public function getCars(&$carCollection = null)
{
$carCollection = new CarCollection([]);
}
it works without any problems. Seems like the callback is somehow the problem.
Whoever downvoted this, can you please elaborate why and why you voted to close?
I might be misunderstanding the question, but you should be able to modify an object when passing by reference. See here for an example: https://3v4l.org/KtFvZ
In the later example code that you added, you shouldn't pass $carCollection by reference, the & should only be in the method/function defintion, not provided when you call it. I don't think that is your problem though, that should be throwing an error in php7.

When should one pass an object as a parameter vs instantiating?

I am curious about the best practices and any performance or other considerations relating to passing an instance of an object as a parameter to another function in the same class vs creating another instance of that object in the new function. Here's a quick example:
Option 1: Pass both instance of Trainee AND TraineeController to other functions
protected function startTraining($traineeID) {
$traineeController = new TraineeController();
$trainee = $traineeController->findTrainee($traineeID);
$this->initializeTraining($trainee, $traineeController);
$this->doSomeOtherStuffWithTrainee($trainee, $traineeController);
return Redirect::back()->with('trainee', $trainee);
}
protected function initializeTraining($trainee, $traineeController) {
$trainee->blah1 = 'red';
$trainee->blah2 = 'blue';
$propertiesToUpdate = [
'blah1' => $trainee->blah1,
'blah2' => $trainee->blah2
];
$traineeController->updateTrainee($trainee->traineeID, $propertiesToUpdate);
}
Option 2: Pass $trainee ONLY, instantiate a new TaineeController each time
protected function startTraining($traineeID) {
$traineeController = new TraineeController();
$trainee = $traineeController->findTrainee($traineeID);
$this->initializeTraining($trainee);
$this->doSomeOtherStuffWithTrainee($trainee);
return Redirect::back()->with('trainee', $trainee);
}
protected function initializeTraining($trainee) {
$trainee->blah1 = 'red';
$trainee->blah2 = 'blue';
$propertiesToUpdate = [
'blah1' => $trainee->blah1,
'blah2' => $trainee->blah2
];
$traineeController = new TraineeController();
$traineeController->updateTrainee($trainee->traineeID, $propertiesToUpdate);
}
In the above I need to pass $trainee across all functions each time instead of creating a new trainee from $traineeID because some other stuff goes on behind the scenes during the 'training' process that would otherwise be lost before relevant data is saved to the db. However, this is not required for TraineeController - I can either pass it as a parameter or instantiate a new TraineeController as much as I want. Which is the better choice?
I saw this question relating to C#, where the accepted answer was that passing an entire object is usually more efficient and instantiating another one because you are passing by reference. Does this hold true for PHP? Ie is the most efficient approach to pass the entire object by reference to required functions using &?
There is nothing wrong with passing an object as reference, but note that php expects that your function argument needs to expect a reference rather than just passing a variable by reference (php docs). php 5.4.0 will even raise a fatal error if this is not respected:
right:
protected function initializeTraining($trainee, &$traineeController) {}
$this->initializeTraining($trainee, $traineeController);
wrong:
protected function initializeTraining($trainee, $traineeController) {}
$this->initializeTraining($trainee, &$traineeController);
Passing objects by reference will in most cases have better performance than initiating the object again, but passing by reference could become tricky if your object has its own properties:
class TraineeController {
$fooCalled = false;
function foo(){ $this->fooCalled = true; }
function isFooCalled(){ return $this->fooCalled; }
}
$traineeController = new TraineeController();
$traineeController->foo();
//&$traineeController->isFooCalled() will be different from
//new TraineeController()->isFooCalled().

Using $this when not in object context RethinkDB time object

I'm trying to convert an object that holds a rethinkdb datetime object (provided by the PHP-RQL library) but i'm getting a fatal error saying:
Using $this when not in object context
This is an overview of the code:
$day = new stdClass;
$datetime = new DateTime();
$arrival = r\time(
$datetime->format('Y'),
$datetime->format('m'),
$datetime->format('d'),
$datetime->format('H'),
$datetime->format('i'),
$datetime->format('s'),
'Z'
);
$day->arrival = $arrival;
$day = object_to_array($day);
It is in the object_to_array() function I get the fatal error, its code is:
function object_to_array($obj) {
$array = array(); // noisy $array does not exist
$arrObj = is_object($obj) ? get_object_vars($obj) : $obj;
foreach ($arrObj as $key => $val) {
$val = (is_array($val) || is_object($val)) ? $this->getArray($val) : $val;
$array[$key] = $val;
}
return $array;
}
I don't remember where I got this function came from (its not mine) but its served me well in the past.
Essentially my question is why does this object_to_array function fail?
This is what the r\time function returns (an object): https://gist.github.com/fenfe1/6676924
Note: Converting just the time object to an array works fine, but passing an object containing the time fails.
Ultimately I need to end up with an array for use in another function and as other properties will be added to the day object it would be beneficial to keep this as an object.
The error message
Using $this when not in object context
already tells you the reason why the function fails. You use $this. $this is normally used inside a class as a reference to the instantiated object, but in your case you use a simple function, so there is no object context.

How to test for OAuth object?

I have a function that accepts an OAuth object:
$oauth = new OAuth("abc","def",OAUTH_SIG_METHOD_HMACSHA1,OAUTH_AUTH_TYPE_AUTHORIZATION);
function get_oauth_header($oauth, $header)
{
if ( !is_string($header) ) {
return NULL;
}
$result_headers = explode("\r\n", $oauth->getLastResponseHeaders());
// Do something interesting....
}
I would like to add a check at the top of the function to ensure that only valid OAuth objects are passed to the function, as is done with the $header variable being checked that it is a string. How might I check this? I have tried to output the type of an OAuth object with gettype() but it returns 0.
PHP5 introduced a feature that will help you here: type hinting. You can use it to require certain parameters to be instances of a certain class.
function get_oauth_header(OAuth $oauth, $header) {
// ...
}
Note that this will produce a fatal error if the $oauth passed is not an instance of OAuth.
As an alternative, you were sort-of on the right track with gettype(), but this will only report PHP's internal type. What you needed here was instanceof or get_class().
var_dump($oauth instanceof OAuth); // true
var_dump(get_class($oauth) === 'OAuth'); // true

Categories