return objects on the fly in php - php

I know this can be done in javascript like so:
function doSomething(){
var something, something_else, another_thing;
// do something with these vars
return {
attribute1 : something,
array1 : [
something_else,
another_thing
]
}
}
can it be done in php?

You can create a new object of stdClass(), assign its attributes and return it.
$x = new stdClass();
$x->attribute1 = "something";
$x->array1 = array(1,2,3);
var_dump($x);
return $x;

PHP does not support object literals. However, it does have a generic stdClass class, which you can typecast an array into for a somewhat similar syntax.
function doSomething()
{
$something = 1;
$something_else = 2;
$another_thing = 3;
return (object) [
"attribute1" => $something,
"array1" => [
$something_else,
$another_thing
]
];
}
var_dump(doSomething());
will give (demo)
object(stdClass)#1 (2) {
["attribute1"]=> int(1)
["array1"]=> array(2) {
[0]=> int(2)
[1]=> int(3)
}
}
Note that you can only use short array syntax as of PHP 5.4. Before that you'd use array().

Related

How to get all values of an enum in PHP?

PHP 8.1 is almost getting released, including support for Enumerations. I was testing some of the enum functionality and couldn't find much documentation about it. Hence my question: how do I get all values of an enum?
For basic enums:
$suits = array_column(Suit::cases(), 'name');
For backed enums where you want the values:
$suits = array_column(Suit::cases(), 'value');
You could then do something like this:
trait EnumToArray
{
public static function names(): array
{
return array_column(self::cases(), 'name');
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
public static function array(): array
{
return array_combine(self::values(), self::names());
}
}
enum Suit: string
{
use EnumToArray;
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
Suit::array() will return:
Array
(
[H] => Hearts
[D] => Diamonds
[C] => Clubs
[S] => Spades
)
After some research I found the answer. You can use the static method: cases().
enum Status
{
case PAID;
case Cancelled;
}
Status::cases();
The cases method will return an array with an enum (UnitEnum interface) for each value.
Need the values and not Enum instances?
I've written a Composer package for this, othyn/php-enum-enhancements, as the UnitEnum::cases() method wasn't what I was looking for, as that returns an array of MySuperCoolEnum instances instead of the underlying values as their raw type, which is what I wanted.
Its a trait that can be easily added to any enum that does the following:
Adds a new static UnitEnum::valueArray(): array method that returns all values within an Enum as an equally typed array of Enum values
Adds a new static UnitEnum::valueList(string $separator = ', '): string method that returns all values within an Enum as a comma separated list string
In which produces the following for normal Enum's:
<?php
namespace App\Enums;
use Othyn\PhpEnumEnhancements\Traits\EnumEnhancements;
enum TestEnum
{
use EnumEnhancements;
case Alpha;
case Bravo;
case Charlie;
case Delta;
case Echo;
}
var_dump(TestEnum::valueArray());
// Results in the following being printed:
// array(5) {
// [0]=>
// string(5) "Alpha"
// [1]=>
// string(5) "Bravo"
// [2]=>
// string(7) "Charlie"
// [3]=>
// string(5) "Delta"
// [4]=>
// string(4) "Echo"
// }
var_dump(TestEnum::valueList());
// Results in the following being printed:
// string(34) "Alpha, Bravo, Charlie, Delta, Echo"
var_dump(TestEnum::valueList(separator: ':'));
// Results in the following being printed:
// string(30) "Alpha:Bravo:Charlie:Delta:Echo"
... and the following for Backed Enum's, the following being a string example:
<?php
namespace App\Enums;
use Othyn\PhpEnumEnhancements\Traits\EnumEnhancements;
enum TestStringBackedEnum: string
{
use EnumEnhancements;
case Alpha = 'alpha';
case Bravo = 'bravo';
case Charlie = 'charlie';
case Delta = 'delta';
case Echo = 'echo';
}
var_dump(TestStringBackedEnum::valueArray());
// Results in the following being printed:
// array(5) {
// [0]=>
// string(5) "alpha"
// [1]=>
// string(5) "bravo"
// [2]=>
// string(7) "charlie"
// [3]=>
// string(5) "delta"
// [4]=>
// string(4) "echo"
// }
var_dump(TestStringBackedEnum::valueList());
// Results in the following being printed:
// string(34) "alpha, bravo, charlie, delta, echo"
var_dump(TestStringBackedEnum::valueList(separator: ':'));
// Results in the following being printed:
// string(30) "alpha:bravo:charlie:delta:echo"
... and yes it works on int's too!
There are more examples in the Usage part of the package's README.
In addition to UnitEnum::cases() you can use ReflectionEnum with this
$reflection = new ReflectionEnum(Status::class);
$reflection->getCases();
note that in both cases you will not be able to get the enum methods. but as long as the ReflectionEnum is extending the ReflectionClass so you can use the rest of ReflectionClass methods such as getMethods
I think the best options is using a trait for that.
For example:
EnumsToArray.php
<?php
namespace App\Traits;
trait EnumsToArray {
public static function toArray(): array {
return array_map(
fn(self $enum) => $enum->value,
self::cases()
);
}
}
And later, in you enum you should have:
use App\Traits\EnumsToArray;
Enum Currency: string {
use EnumsToArray;
case DOLLAR = "usd";
case EURO = "eur";
}
I have used the following in my project;
public static function toAssociativeArray(): array
{
foreach(self::cases() as $case) {
$array[$case->value] = $case->name;
}
return $array;
}
Which results in an associative array like this;
using strings as values
enum DiaryRole: string
{
case DANGER = 'red';
case WARNING = 'yellow';
case SAFE = 'green';
}
$array = [
'red' => 'DANGER',
'yellow' => 'WARNING',
'green' => 'SAFE'
];
or when using integers as values
enum DiaryRole: int
{
case DANGER = 1;
case WARNING = 2;
case SAFE = 3;
}
$array = [
1 => 'DANGER',
2 => 'WARNING',
3 => 'SAFE'
];
You can now use the array to get any information you need, and you can get only the columns or values using array_keys() or array_values()
I have used this code to easily foreach through them in a form select
I wrapped a slitly changed approach from #Michael up in a small package, cause I needed it in multiple projects:
https://github.com/laracraft-tech/laravel-useful-traits#usefulenums
Install via composer:
composer require laracraft-tech/laravel-useful-traits
This is how it is working:
use LaracraftTech\LaravelUsefulTraits\UsefulEnums;
enum PaymentType: int
{
use UsefulEnums;
case Pending = 1;
case Failed = 2;
case Success = 3;
}
PaymentType::names(); // return ['Pending', 'Failed', 'Success']
PaymentType::values(); // return [1, 2, 3]
PaymentType::array(); // return ['Pending' => 1, 'Failed' => 2, 'Success' => 3]

PHP get keys from array in object

I've got object and I need list of it's keys.
for now I doing that in foreach
foreach($obj as $key => $attribute){
var_dump($key);
}
Is there some PHP built in function for getting object keys like array_keys for arrays?
trace
object(Solarium\QueryType\Select\Result\Document)#1383 (1) {
["fields":protected]=> array(31) { ["pdf_url"]=> string(51)
"xxxxxxxxxxxx" ["title"]=>
string(150) ......
class A
{
private $a = 1;
protected $b = 2;
public $c = 3;
}
$object = new A();
$fields = get_object_vars($object);
But by this method, you can only get public fields from your object,
i.e
print_r($fields);
Will output
Array ( [c] => 3 )
Problem is because it was
array in object.
I solve problem with this
array_keys($obj->getFields())

Can I set the keys of an array using array functions like array_map

I really like the functional programming style of using array map to create an array of objects from another array of objects.
$newObjects = array_map(
function($oldObject) {
return new NewObject($oldObject);
},
$oldObjects
);
Which all works fine but I would really like to be able to set the indices of the array so that they are the ids of the original objects for easier search and retrieval from the array but I cannot think how to do it other then which is not as elegant.
$newObjects = array();
foreach ($oldObjects as $oldObject) {
$newObjects[$oldObject->getId()] = new NewObject($oldObject);
}
Is there a way I can do this?
That is - array_reduce() is exactly what you need:
class Bar
{
protected $id;
public function __construct($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
}
class Foo
{
protected $bar;
public function __construct(Bar $bar)
{
$this->bar = $bar;
}
}
$oldObjects = [new Bar('x'), new Bar('y'), new Bar('z')];
$newObjects = array_reduce($oldObjects, function($current, Bar $obj) {
$current[$obj->getId()] = new Foo($obj);
return $current;
}, []);
This will do all in-place without having to spend memory on additional arrays like for array_combine()
However, I would suggest to use such constructs when they're necessary. Using this just because it "looks better" might be not a good idea - as plain loops are in most cases just more readable.
What if you use array_walk and a temporary array with your new indices.
$array = ['A', 'B', 'C', 'D'];
$reIndexedTemp = [];
array_walk(
$array,
function ($item, $key) use (&$reIndexedTemp) {
// here you can have your logic to assemble your new index
$reIndexedTemp[$key + 100] = $item;
}
);
//$array = $reIndexedTemp;
var_dump($array, $reIndexedTemp);
output (without the commented line) :
array(4) {
[0] =>
string(1) "A"
[1] =>
string(1) "B"
[2] =>
string(1) "C"
[3] =>
string(1) "D"
}
array(4) {
[100] =>
string(1) "A"
[101] =>
string(1) "B"
[102] =>
string(1) "C"
[103] =>
string(1) "D"
}
I think a foreach is probably the most readable solution in this case, but you can use array_map() with array_combine() to achieve what you want. Something like:
// empty array to store the old object ids
$ids = [];
// map over old objects, inheriting $id
// from parent scope by reference
$objs = array_map(function($oldObject) use (&$ids) {
$ids[] = $oldObject->getId();
return new NewObject($oldObject);
}, $oldObjects);
// combine id and object arrays
$newObjects = array_combine($ids, $objs);
Hope this helps :)
Looking around - Looking for array_map equivalent to work on keys in associative arrays
Suggests it might work using array_combine
So I guess it would be
$newObjects = array_combine(
array_map(
function($oldObject) {
return $oldObject->getId();
},
$oldObjects
),
array_map(
function($oldObject) {
return new NewObject($oldObject);
},
$oldObjects
)
);
Hmm probably the best, just this side of overblown but definately a lot more complex than the foreach

In PHP, how can I add an object element to an array?

I'm using PHP. I have an array of objects, and would like to add an object to the end of it.
$myArray[] = null; //adds an element
$myArray[count($myArray) - 1]->name = "my name"; //modifies the element I just added
The above is functional, but is there a cleaner and more-readable way to write that? Maybe one line?
Just do:
$object = new stdClass();
$object->name = "My name";
$myArray[] = $object;
You need to create the object first (the new line) and then push it onto the end of the array (the [] line).
You can also do this:
$myArray[] = (object) ['name' => 'My name'];
However I would argue that's not as readable, even if it is more succinct.
Here is a clean method I've discovered:
$myArray = [];
array_push($myArray, (object)[
'key1' => 'someValue',
'key2' => 'someValue2',
'key3' => 'someValue3',
]);
return $myArray;
Do you really need an object? What about:
$myArray[] = array("name" => "my name");
Just use a two-dimensional array.
Output (var_dump):
array(1) {
[0]=>
array(1) {
["name"]=>
string(7) "my name"
}
}
You could access your last entry like this:
echo $myArray[count($myArray) - 1]["name"];
Something like:
class TestClass {
private $var1;
private $var2;
private function TestClass($var1, $var2){
$this->var1 = $var1;
$this->var2 = $var2;
}
public static function create($var1, $var2){
if (is_numeric($var1)){
return new TestClass($var1, $var2);
}
else return NULL;
}
}
$myArray = array();
$myArray[] = TestClass::create(15, "asdf");
$myArray[] = TestClass::create(20, "asdfa");
$myArray[] = TestClass::create("a", "abcd");
print_r($myArray);
$myArray = array_filter($myArray, function($e){ return !is_null($e);});
print_r($myArray);
I think that there are situations where this constructions are preferable to arrays. You can move all the checking logic to the class.
Here, before the call to array_filter $myArray has 3 elements. Two correct objects and a NULL. After the call, only the 2 correct elements persist.

Using json_encode on objects in PHP (regardless of scope)

I'm trying to output lists of objects as json and would like to know if there's a way to make objects usable to json_encode? The code I've got looks something like
$related = $user->getRelatedUsers();
echo json_encode($related);
Right now, I'm just iterating through the array of users and individually exporting them into arrays for json_encode to turn into usable json for me. I've already tried making the objects iterable, but json_encode just seems to skip them anyway.
edit: here's the var_dump();
php > var_dump($a);
object(RedBean_OODBBean)#14 (2) {
["properties":"RedBean_OODBBean":private]=>
array(11) {
["id"]=>
string(5) "17972"
["pk_UniversalID"]=>
string(5) "18830"
["UniversalIdentity"]=>
string(1) "1"
["UniversalUserName"]=>
string(9) "showforce"
["UniversalPassword"]=>
string(32) ""
["UniversalDomain"]=>
string(1) "0"
["UniversalCrunchBase"]=>
string(1) "0"
["isApproved"]=>
string(1) "0"
["accountHash"]=>
string(32) ""
["CurrentEvent"]=>
string(4) "1204"
["userType"]=>
string(7) "company"
}
["__info":"RedBean_OODBBean":private]=>
array(4) {
["type"]=>
string(4) "user"
["sys"]=>
array(1) {
["idfield"]=>
string(2) "id"
}
["tainted"]=>
bool(false)
["model"]=>
object(Model_User)#16 (1) {
["bean":protected]=>
*RECURSION*
}
}
}
and here's what json_encode gives me:
php > echo json_encode($a);
{}
I ended up with just this:
function json_encode_objs($item){
if(!is_array($item) && !is_object($item)){
return json_encode($item);
}else{
$pieces = array();
foreach($item as $k=>$v){
$pieces[] = "\"$k\":".json_encode_objs($v);
}
return '{'.implode(',',$pieces).'}';
}
}
It takes arrays full of those objects or just single instances and turns them into json - I use it instead of json_encode. I'm sure there are places I could make it better, but I was hoping that json_encode would be able to detect when to iterate through an object based on its exposed interfaces.
All the properties of your object are private. aka... not available outside their class's scope.
Solution for PHP >= 5.4
Use the new JsonSerializable Interface to provide your own json representation to be used by json_encode
class Thing implements JsonSerializable {
...
public function jsonSerialize() {
return [
'something' => $this->something,
'protected_something' => $this->get_protected_something(),
'private_something' => $this->get_private_something()
];
}
...
}
Solution for PHP < 5.4
If you do want to serialize your private and protected object properties, you have to implement a JSON encoding function inside your Class that utilizes json_encode() on a data structure you create for this purpose.
class Thing {
...
public function to_json() {
return json_encode(array(
'something' => $this->something,
'protected_something' => $this->get_protected_something(),
'private_something' => $this->get_private_something()
));
}
...
}
A more detailed writeup
In PHP >= 5.4.0 there is a new interface for serializing objects to JSON : JsonSerializable
Just implement the interface in your object and define a JsonSerializable method which will be called when you use json_encode.
So the solution for PHP >= 5.4.0 should look something like this:
class JsonObject implements JsonSerializable
{
// properties
// function called when encoded with json_encode
public function jsonSerialize()
{
return get_object_vars($this);
}
}
In RedBeanPHP 2.0 there is a mass-export function which turns an entire collection of beans into arrays. This works with the JSON encoder..
json_encode( R::exportAll( $beans ) );
Following code worked for me:
public function jsonSerialize()
{
return get_object_vars($this);
}
I didn't see this mentioned yet, but beans have a built-in method called getProperties().
So, to use it:
// What bean do we want to get?
$type = 'book';
$id = 13;
// Load the bean
$post = R::load($type,$id);
// Get the properties
$props = $post->getProperties();
// Print the JSON-encoded value
print json_encode($props);
This outputs:
{
"id": "13",
"title": "Oliver Twist",
"author": "Charles Dickens"
}
Now take it a step further. If we have an array of beans...
// An array of beans (just an example)
$series = array($post,$post,$post);
...then we could do the following:
Loop through the array with a foreach loop.
Replace each element (a bean) with an array of the bean's properties.
So this...
foreach ($series as &$val) {
$val = $val->getProperties();
}
print json_encode($series);
...outputs this:
[
{
"id": "13",
"title": "Oliver Twist",
"author": "Charles Dickens"
},
{
"id": "13",
"title": "Oliver Twist",
"author": "Charles Dickens"
},
{
"id": "13",
"title": "Oliver Twist",
"author": "Charles Dickens"
}
]
Hope this helps!
I usually include a small function in my objects which allows me to dump to array or json or xml. Something like:
public function exportObj($method = 'a')
{
if($method == 'j')
{
return json_encode(get_object_vars($this));
}
else
{
return get_object_vars($this);
}
}
either way, get_object_vars() is probably useful to you.
$products=R::findAll('products');
$string = rtrim(implode(',', $products), ',');
echo $string;
Here is my way:
function xml2array($xml_data)
{
$xml_to_array = [];
if(isset($xml_data))
{
if(is_iterable($xml_data))
{
foreach($xml_data as $key => $value)
{
if(is_object($value))
{
if(empty((array)$value))
{
$value = (string)$value;
}
else
{
$value = (array)$value;
}
$value = xml2array($value);
}
$xml_to_array[$key] = $value;
}
}
else
{
$xml_to_array = $xml_data;
}
}
return $xml_to_array;
}
for an array of objects, I used something like this, while following the custom method for php < 5.4:
$jsArray=array();
//transaction is an array of the class transaction
//which implements the method to_json
foreach($transactions as $tran)
{
$jsArray[]=$tran->to_json();
}
echo json_encode($jsArray);

Categories