Does unset before the assignment matter? - php

class RouteCollection implements \IteratorAggregate, \Countable
{
/**
* #var Route[]
*/
private $routes = array();
public function add($name, Route $route)
{
unset($this->routes[$name]);
$this->routes[$name] = $route;
}
public function remove($name)
{
foreach ((array) $name as $n) {
unset($this->routes[$n]);
}
}
}
This is a piece of code from the class Symfony\Component\Routing\RouteCollection. Does unset before the assignment matter?
Why is it done?
Second question: Why in remove method simple string is parsed to array?
Why I can't use simply:
unset($this->routes[$name]);
Same as in add method?

Does unset before the assignment matter?
It can. If $this->routes[$name] is a PHP reference and you don't use unset, all symbols pointing to the underlying value will point to the new value. If you use unset before, the assignment will only affect the symbol used.
Second question: Why in remove method simple string is parsed to array?
This is just a small trick that allows you to use the remove method with both strings and array. When you cast a string (e.g. 'abc') to an array, PHP will return an array that contains the string as its only value (array(0 => 'abc')). For strings the method works as you suggested, and for arrays it will unset all names in the array.

Related

unsetting objects in an array based on a bool method

I'm trying to filter an array of objects implementing a specific interface (which simply defines the isComplete(): bool method) based on the result of that method. array_filter doesn't work because it can't call a method on each object to determine whether to filter it (or can it?). I've tried writing a function that takes the splatted array as an argument by reference, this doesn't work either:
function skipIncomplete(CompletableObjectInterface &...$objects): array {
$skipped = [];
foreach ($objects as $index => $item) {
if (!$item->isComplete()) {
$skipped[] = $item->id ?? $index;
unset($objects[$index]);
}
}
return $skipped;
}
The original elements passed in simply don't end up getting unset.
I'm looking for a way that doesn't include creating an entirely new Collection class to hold my CompletableObjects for complexity reasons. I really want to keep the type hint so no one can pass in a generic array, causing runtime errors when the function tries to call $item->isComplete.
Is there any way I can achieve this in PHP 7.3.15?
Added a filter, please comment as to what is wrong with this type of approach:
<?php
interface CompletableObjectInterface {
public function isComplete() : bool;
}
class Foo implements CompletableObjectInterface
{
public function isComplete() : bool
{
return false;
}
}
class Bar implements CompletableObjectInterface
{
public function isComplete() : bool
{
return true;
}
}
$foo = new Foo;
$bar = new Bar;
$incomplete = array_filter([$foo, $bar], function($obj) { return !$obj->isComplete();});
var_dump($incomplete);
Output:
array(1) {
[0]=>
object(Foo)#1 (0) {
}
}
Looks like you got a bit hung up on a wrong understanding of the ... syntax for a variable number of arguments.
You are passing in one array, and the $objects parameter will therefore contain that array in the first index, i.e. in $objects[0].
So in theory you could just change your line
unset($objects[$index]);
to
unset($objects[0][$index]);
However, I do not really see why the variable number of arguments syntax is used at all, since you apparently are just expecting one array of values (objects in this case) as an argument to the function. Therefore I'd recommend you just remove the ... from the argument list and your solution does what you wanted.
Alternatively you can of course add an outer foreach-loop and iterate over all passed "arrays of objects", if that is an use case for you.

Shorthand if madness, is it possible to rewrite even smarter?

I have this structure (currently working on PHP 7.4)
/** #var bool $shouldBeEmpty */
/** #var array $array */
if ($shouldBeEmpty) {
$array = [];
} else {
$array = $data['some_data'] ?? [];
}
It could be rewritten in one line like this
$array = $shouldBeEmpty ? [] : $data['some_data'] ?? [];
Question is - is this possible to get rid of one [] and do the same with only one [].
Assigning [] to variable or replacing one with array() won't be the case.
Is there only option that $shouldBeEmpty should be empty/non-empty array so I could reuse it? Boolean won't work in any case?
Actually, example is quite terrible, but in real class it looks a bit better.
Also, this code is executed in OOP, of course, and it will make sense to use example from a class.
As well as what I realised, that I could simply use preseted value from the class property, so I don't have to change it if I don't need to. So, here is the example, I am quite happy with.
class MyClass
{
protected $collectedData = [];
public function __construct($data)
{
if (!$data['shouldBeEmpty']) {
$this->collectedData = $data['some_data'] ?? [];
}
}
}

Check for undefined PHP method before calling?

What can I use other than if(!empty( $product->a_funky_function() )) to check if the method is empty before calling it?
I've tried method_exists() and function_exists() and a whole plethora of conditions. I think the issue is that I need to have my $product variable there.
Please help.
A fairly common pattern is to have two methods on your class, along the lines of getField and hasField. The former returns the value, and the latter returns true or false depending whether or not the value is set (where "set" can mean not null, or not empty, or whatever else you might want it to mean).
An example:
class Foo
{
/** #var string */
private $field;
/**
* #return string
*/
public function getField()
{
return $this->field;
}
/**
* #return bool
*/
public function hasField()
{
return $this->getField() !== null;
}
}
This would then be used like:
if ($foo->hasField()) {
$field = $foo->getField();
...
}
Often, like in this example, the has... method often just delegates to the get... method internally, to save duplicating the logic. If the getter contains particularly heavy processing (e.g. a database lookup or API call), you might want to factor in ways to avoid performing it twice, but that's a bit out of scope.
You need absolutely to call it so it can compute the value which it will return, so I think there is no other way to know if it will return something not empty without calling it...
You have to call it.
$result = $product->getFunction();
if(!empty($result)) {
//your code here
}

How to receive specific interface's array in PHP class method?

I want to receive array of MyInterface as the in the below code.
public function saveMultiple(
\Path\To\MyInterface $attachLinks[]
);
The above code doesn't work.
So please don't tell me that just remove \Path\To\MyInterface and to use a variable $attachLinks. I'm already aware of that but this is something which I require.
There are no generic types in php, so you can not specify type «array of something».
But you may do a trick. Check types of elements in array.
array_walk($attachLinks, function (\Path\To\MyInterface $item) {});
If you wrap it in assert, you will be able to disable this check.
assert('array_walk($attachLinks, function (\Path\To\MyInterface $item) {})');
So far this is what can be done using PHP7. Till date passing arrays with specific type is not possible.
public function saveMultiple(
array $attachLinks
);
It'll now at least make sure that the method only gets array as parameter.
Maybe you can use some type of setter like:
private $attachLinks = [];
public function setter(MyInterface $var)
{
$this->attachLinks[] = $var;
}
And than use a $this->attachLinks in your function
public function saveMultiple() {
print_r($this->attachLinks);
}

What does the function parameter without a dollar sign mean?

I've been studying PHP OOP and I came across this interesting function declaration:
class ShopProductWriter {
public $products = array();
public function addProduct(ShopProduct $shopProduct) {
//code here..
}
}
notice how the first parameter of the addProduct function ShopProduct doesn't have a dollar sign? What does it mean?
it is not a parameter. it is the type definition of $shopProduct. this function takes only 1 parameter so $shopProduct can only be a ShopProduct class
It's typehinting.
A parameter-list has to be comma-separated. This stands in front of a parameter and declares the parameter's type.
Your example typehint is for an object of the class ShopProduct.
Meaning: If something else was passed, like an integer or an array, the method would throw an exception. (It reflects inheritance. Assume there was an instance of SpecificShopProduct, a class that would be extending ShopProduct, you could pass that as well.)
It is possible to typehint objects and an array, an Class or an Interface. It is not possible to typehint normal variables types like integers or a string, and you cannot typehint an array of objects.
public function foo(array someArray, LoggerInterface $logger)
{
}
Yet for some of the other types you could use phpDoc comments, yet those are for readability and IDE support, they would not enforce a type; meaning you would have to take to check for yourself that a correct type was passed.
/**
* #param ShopProductWriter[] arrayOfObjects
* #param int $someInt
* #param string $someString
*/
public function foo(array arrayOfObjects, $someInt, $someString)
{
}

Categories