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

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'] ?? [];
}
}
}

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.

Class-Wide accessible static array, trying to push to it, but keeps coming back empty

class UpcomingEvents {
//Variable I'm trying to make accessible and modify throughout the class methods
private static $postObjArr = array();
private static $postIdArr = array();
private static $pinnedPost;
//My attempt at a get method to solve this issue, it did not
private static function getPostObjArr() {
$postObjArr = static::$postObjArr;
return $postObjArr;
}
private static function sortByDateProp($a, $b) {
$Adate = strtotime(get_field('event_date',$a->ID));
$Bdate = strtotime(get_field('event_date',$b->ID));
if ($Adate == $Bdate) {
return 0;
}
return ($Adate < $Bdate) ? -1 : 1;
}
private static function queryDatesAndSort($args) {
$postQuery = new WP_Query( $args );
if( $postQuery->have_posts() ) {
while( $postQuery->have_posts() ) {
$postQuery->the_post();
//Trying to push to the array, to no avail
array_push(static::getPostObjArr(), get_post());
}
}
//Trying to return the array after pushing to it, comes back empty
return(var_dump(static::getPostObjArr()));
//Trying to sort it
usort(static::getPostObjArr(), array(self,'sortByDateProp'));
foreach (static::getPostObjArr() as $key => $value) {
array_push(static::$postIdArr, $value->ID);
}
}
}
I'm trying to access $postObjArr within the class, and push to it with the queryDatesAndSort(); method. I've tried a couple of things, most recent being to use a get method for the variable. I don't want to make it global as it's bad practice I've heard. I've also tried passing by reference I.E
&static::$postObjArr;
But when it hits the vardump, it spits out an empty array. What would be the solution and best practice here? To allow the class methods to access and modify a single static array variable.
static::$postObjArr[] = get_post()
I didn't think it would of made a difference, but it worked. Can you explain to me why that worked but array.push(); Did not?
Arrays are always copy-on-write in PHP. If you assign an array to another variable, pass it into a function, or return it from a function, it's for all intents and purposes a different, new array. Modifying it does not modify the "original" array. If you want to pass an array around and continue to modify the original array, you'll have to use pass-by-reference everywhere. Meaning you will have to add a & everywhere you assign it to a different variable, pass it into a function, or return it from a function. If you forget your & anywhere, the reference is broken.
Since that's rather annoying to work with, you rarely use references in PHP and you either modify your arrays directly (static::$postObjArr), or you use objects (stdClass or a custom class) instead which can be passed around without breaking reference.

Does unset before the assignment matter?

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.

PHP: Treat arrays like objects (like in js)

In JS you could have something like this:
var a = [1, 2, 3];
a.myProp = false;
This is possible because in js array are actually objects.
What I'm trying to do is to implement the same, but in PHP.
The closest solution I've found is to use the ArrayObject class, but the main problem of this "solution" is that it is object and when you check with is_array or try to use other specific array method it will return false or throw error.
Is there any magic php method like __construct or sth similar for arrays?
In general - I want to add some kind of a flag separated from the array values. For example:
$a = [1, 2, 3];
$a.alreadyIterated = false;
foreach ($a as $item) {
...
}
$a.alreadyIterated = true;
...
if (!$a.alreadyIterated) {
foreach($a as $item) {...}
}
This is somewhat possible via creating your own class:
class PropArray implements ArrayAccess, Iterator {
public $alreadyIterated = false;
protected $a = []; # internal array for iterable values
# add required methods from the interfaces, pretty easy
}
Instances will still be objects, but can be iterated like arrays and tested via is_array($obj) || $obj instanceof Traversable.

Symfony 2 / Php : json_encode

I've been looking to a solution to my problem for a while without success so I'm asking here.
How can we return a json-encoded result on an array of objects (or just an object) containing private properties ?
Indeed, when you use json_encode($myObject), it won't display the private or protected properties, which are present everywhere in the model when using Symfony...
I'm surprised I couldn't find any method like json_encode that would call getters instead of properties themselves.
Any idea ?
EDIT
In that case I would rather do a unique function that looks like :
public function toArray() {
$vars = get_object_vars($this);
$result = array();
foreach ($vars as $key => $value) {
if (is_object($value)) {
$result[$key] = toArray($value);
} else {
$result[$key] = $value;
}
}
return $result;
}
in order to avoid rewriting every property name everytime...
But anyway I think I'll just create an array containing the vars I need, so that I won't touch the model (which is generated code).
Have you try GetSetMethodNormalizer ?
http://api.symfony.com/2.0/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.html
Ex. https://stackoverflow.com/a/6709828/520114
Right now there is no way for this. Only php serialize/unserialize handles the true serialisation of objects.
You'll have to implement them yourselve, or rather let objects return their json values themselves.
You will have to implement your own method toArray() where you expose all your private values in an array:
public function toArray()
{
return array(
'property1' => $this->myproperty1,
'property2' => $this->myproperty2
);
}
And call it like this:
json_encode(MyObject->toArray());
[Edit: this question is not about doctrine, but since you mention both symfony2 and the model, you can consider using Array Hydration for your model: http://www.doctrine-project.org/docs/orm/2.0/en/reference/dql-doctrine-query-language.html#array-hydration ]

Categories