How do I fill an object in PHP from an Array - php

Suppose I have:
class A{
public $one;
public $two;
}
and an array with values:
array('one' => 234, 'two' => 2)
is there a way to have an instance of A filled with the right values from the array automatically?

You need to write yourself a function for that. PHP has get_object_varsDocs but no set counterpart:
function set_object_vars($object, array $vars) {
$has = get_object_vars($object);
foreach ($has as $name => $oldValue) {
$object->$name = isset($vars[$name]) ? $vars[$name] : NULL;
}
}
Usage:
$a = new A();
$vars = array('one' => 234, 'two' => 2);
set_object_vars($a, $vars);

If you want to allow for bulk-setting of attributes, you can also store them as a property. It allows you to encapsulate within the class a little better.
class A{
protected $attributes = array();
function setAttributes($attributes){
$this->attributes = $attributes;
}
public function __get($key){
return $this->attributes[$key];
}
}

#hakre version is quite good, but dangerous (suppose an id or password is in thoses props).
I would change the default behavior to that:
function set_object_vars($object, array $vars) {
$has = get_object_vars($object);
foreach ($has as $name => $oldValue) {
array_key_exists($name, $vars) ? $object->$name =$vars[$name] : NULL;
}
}
here, the previous properties that are not in the $vars array are not affected.
and if you want to set a prop to null on purpose, you can.

Yes there is.
You could use a pass thru method.
For example:
class A {
public $one, $tow;
function __construct($values) {
$this->one = $values['one'] ?: null;
$this->two = $values['two'] ?: null;
}
}
$a = new A(array('one' => 234, 'two' => 2));

Related

Dynamic assignment of property names based on array values

I am trying to create a class that is going to generate dynamic class properties according to a user input.
There will be an array created from user input data. This array should work as an example:
$array = array(
# The boolean values are not relevant in this example
# The keys are important
'apple' => true,
'orange' => false,
'pear' => false,
'banana' => true,
);
Right now I want to create a new class with the array keys as the class properties:
class Fruit {
public $apple;
public $orange;
public $pear;
public $banana;
(etc.)
}
I had to manually write down all four properties now.
Is there a way to make it automated?
<?php
class MyClass
{
public function __construct ($config = [])
{
foreach ($config as $key => $value) {
$this->{$key} = $value;
}
}
}
$myClass = new MyClass(['apple' => 1, 'orange' => 2]);
echo $myClass->apple;
?>
this should help you
Here you go,
I put a few bonus things in there:
class MyClass implements Countable, IteratorAggregate
{
protected $data = [];
public function __construct (array $data = [])
{
foreach ($data as $key => $value) {
$this->{$key} = $value;
}
}
public function __set($key, $value){
$this->data[$key] = $value;
}
public function __get($key)
{
if(!isset($this->{$key})) return null; //you could also throw an exception here.
return $this->data[$key];
}
public function __isset($key){
return isset($this->data[$key]);
}
public function __unset($key){
unset($this->data[$key]);
}
public function __call($method, $args){
$mode = substr($method, 0, 3);
$property = strtolower(substr($method, 3)); //only lowercase properties
if(isset($this->{$property})) {
if($mode == 'set'){
$this->{$property} = $args[0];
return null;
}else if($mode == 'get'){
return $this->{$property};
}
}else{
return null; //or throw an exception/remove this return
}
throw new Exception('Call to undefined method '.__CLASS__.'::'.$method);
}
//implement Countable
public function count(){
return count($this->data);
}
//implementIteratorAggregate
public function getIterator() {
return new ArrayIterator($this->data);
}
}
Test it:
$myClass = new MyClass(['one' => 1, 'two' => 2]);
echo $myClass->two."\n";
//Countable
echo count($myClass)."\n";
//dynamic set
$myClass->three = 3;
echo count($myClass)."\n";
//dynamic get/set methods. I like camel case methods, and lowercase properties. If you don't like that then you can change it.
$myClass->setThree(4);
echo $myClass->getThree()."\n";
//IteratorAggregate
foreach($myClass as $key=>$value){
echo $key.' => '.$value."\n";
}
Outputs
2 //value of 2
2 //count of $data
3 //count of $data after adding item
4 //value of 3 after changing it with setThree
//foreach output
one => 1
two => 2
three => 4
Test it online
Disclamer
Generally though it's better to define the class by hand, that way things like IDE's work. You may also have issues because you won't necessarily know what is defined in the class ahead of time. You don't have a concrete definition of the class as it were.
Pretty much any method(at least in my code) that starts with __ is a PHP magic method (yes, that's a thing). When I first learned how to use these I thought it was pretty cool, but now I almost never use them...
Now if you want to create an actual .php file with that code in it, that's a different conversation. (it wasn't 100% clear, if you wanted functionality or an actual file)
Cheers.

Set object properties at constructor call time (PHP)

I wonder if it's possible to achieve similar functionality to C#'s compact instantiation syntax:
itemView.Question = new ItemViewQuestion()
{
AnswersJSON = itemView.Answer.ToJSONString(),
Modified = DateTime.Now,
ModifiedBy = User.Identity.Name
};
I wish to be able to create an object of arbitrary class passing their properties without having to set up constructor code for these properties.
To put up another example, this can be done with stdClass like this:
(object) ["name" => "X", "age" => 30]
Type juggling does not work for custom classes, however.
There is no such functionality natively in PHP, unfortunately.
But you can create a class in your project and extend it in the classes you wish to instantiate without a constructor. Something like this:
<?php
class Fillable{
public static function fill($props)
{
$cls = new static;
foreach($props as $key=>$value){
if (property_exists(static::class,$key)){
$cls->$key = $value;
}
}
return $cls;
}
}
class Vegetable extends Fillable
{
public $edible;
public $color;
}
$veg = Vegetable::fill([
'edible' => true,
'color' => 'green',
'name' => 'potato' //Will not get set as it's not a property of Vegetable. (you could also throw an error/warning here)
]);
var_dump($veg);
Checkout this fiddle for the working example
This is valid in PHP though:
<?php
class Demo {
public function getA() {
return $this->Options['A'];
}
}
$D = new Demo();
$D->Options = Array(
'A' => '1',
'B' => '2',
'C' => '3'
);
var_dump($D->getA());
Or, something like this:
<?php
class Demo {
public function __construct($Options) {
$this->Options = $Options;
}
public function getA() {
return $this->Options['A'];
}
}
$D = new Demo(Array(
'A' => '1',
'B' => '2',
'C' => '3'
));
var_dump($D->getA());
Or even this:
<?php
class Demo {
public function __construct($Options) {
foreach ($Options as $key=>$value) $this->$key = $value;
}
public function getA() {
return $this->A;
}
}
$D = new Demo(Array(
'A' => '1',
'B' => '2',
'C' => '3'
));
var_dump($D->getA());
I guess it really depends what are you trying to achieve? You said you do not want to use magic functions or setters, but is there more to it?
Obviously php doesn't have this. Somewhere a function is required. I did an implementation using a trait which is close.
<?php
Trait Init {
public function init($arr) {
$vars = get_object_vars($this);
foreach($arr as $k => $v) {
if ( array_key_exists($k, $vars) ) $this->$k = $v;
}
}
}
class Demo {
use Init;
public $answersJSON;
public $modified;
public $modifiedBy;
}
$obj = new Demo();
$obj->init(['modified' => 'now']);
print_r($obj);

An object that can be used as an array as well

All over the place I have an array with a few elements, for example:
$myItem = [ 'a' => 10, 'b' => 20 ]
But but I would like to replace it with a class
$myClass = new MyOwnClass( 10, 20 );
$a = $myClass->GetSomeValue(); // this is the old 'a'
$b = $myClass->GetSomeOtherValue(); // this is the old 'b'
but for practical reasons I still want to be able to call
$a = $myClass['a'];
$b = $myClass['b'];
Is something like that possible in php?
Therefore, there is an interface named ArrayAccess. You have to implement it to your class.
class MyOwnClass implements ArrayAccess {
private $arr = null;
public function __construct($arr = null) {
if(is_array($arr))
$this->arr = $arr;
else
$this->arr = [];
}
public function offsetExists ($offset) {
if($this->arr !== null && isset($this->arr[$offset]))
return true;
return false;
}
public function offsetGet ($offset) {
if($this->arr !== null && isset($this->arr[$offset]))
return $this->arr[$offset];
return false;
}
public function offsetSet ($offset, $value) {
$this->arr[$offset] = $value;
}
public function offsetUnset ($offset) {
unset($this->arr[$offset]);
}
}
Use:
$arr = ["a" => 20, "b" => 30];
$obj = new MyOwnClass($arr);
$obj->offsetGet("a"); // Gives 20
$obj->offsetSet("b", 10);
$obj->offsetGet("b"); // Gives 10

Mixed object / array iteration

I need a way to iterate any array or object ($this->_data).
The current draft is following:
class values implements \Iterator{
private $_data = null; // array or object
private $_keys = [];
private $_key = false;
// ------------ \Iterator implementation
public function current(){
return $this->get($this->_key);
}
public function key(){
return $this->_key;
}
public function next(){
$this->_key = \next($this->_keys);
}
public function rewind(){
$this->_keys = [];
foreach ($this->_data as $k => $v){
$this->_keys[] = $k;
}
$this->_key = \reset($this->_keys);
}
public function valid(){
if (false === $this->_key){
return false;
}
return $this->has($this->_key);
}
}
The problem is that i do not want to hold additional array for keys.
May be there are some better way to iterate keys of an object avoiding additional abject/array creation for this purpose?
(External iterator is not an option because i do not want extra object creation in a foreach loops)
Example of mixed usage with native myClass::methods and wrapper's values::methods
class myClass{
var $x = 'x';
var $y = 'y';
public function hello(){
echo 'Hello, '.$this->x;
}
}
$a = new myClass();
$values = new values($a);
foreach ($values as $k => $v){
$values[$k] = $v.' modified';
}
$a->hello();
Additional notes:
values instance can be reused (by replacing $this->_data)
usage of this class can be very intensive (from 100 to over 100000 different objects/arrays passed)
values can be used and/or changed outside of values wrapper (see example above)
While it is possible to implement Iterator on your own in a way that you don't need to store $keys separately (I can show if you want) you can just use or extend the class ArrayObject. It looks like it perfectly fits your needs.
Check this example:
$a = new ArrayObject(array(
'a' => 'foo',
'b' => 'bar'
));
foreach($a as $k => $v) {
var_dump($k, $v);
}
Output:
string(1) "a"
string(3) "foo"
string(1) "b"
string(3) "bar"
For PHP 5.5.0+
Well, it actually fails, because it will create Generator instance at every call
class values implements IteratorAggregate{
private $_data = null; // array or object
public function __construct($data = null){
$this->_data = $data;
}
private static function g($data){
foreach ($data as $k => $v){
yield $k => $v;
}
}
public function getIterator(){
return self::g($this->_data);
}
}
Wish some alternative for php 5.4

How to use __get() to return null in multilevel object property accessing?

How can I use __get() to return null in multilevel object property accessing the case like this below?
For instance, this is my classes,
class property
{
public function __get($name)
{
return (isset($this->$name)) ? $this->$name : null;
}
}
class objectify
{
public function array_to_object($array = array(), $property_overloading = false)
{
# if $array is not an array, let's make it array with one value of former $array.
if (!is_array($array)) $array = array($array);
# Use property overloading to handle inaccessible properties, if overloading is set to be true.
# Else use std object.
if($property_overloading === true) $object = new property();
else $object = new stdClass();
foreach($array as $key => $value)
{
$key = (string) $key ;
$object->$key = is_array($value) ? self::array_to_object($value, $property_overloading) : $value;
}
return $object;
}
}
How I use it,
$object = new objectify();
$type = array(
"category" => "admin",
"person" => "unique",
"a" => array(
"aa" => "xx",
"bb"=> "yy"
),
"passcode" => false
);
$type = $object->array_to_object($type,true);
var_dump($type->a->cc);
result,
null
but I get an error message with NULL when the input array is null,
$type = null;
$type = $object->array_to_object($type,true);
var_dump($type->a->cc);
result,
Notice: Trying to get property of non-object in C:\wamp\www\test...p on line 68
NULL
Is it possible to return NULL in this kind of scenario?
Yes it is, but it's not so trivial to explain how. First understand why you run into that problem:
$value = $a->b->c;
This will first return NULL for $a->b. So actually you wrote:
$value = NULL->c;
So instead of NULL on an unset item you need to return a NULL-object (let's namne it NULLObect) that works similar to your base class and that represents NULL.
As you can imagine, you can not really simulate NULL with it in PHP and PHP's language features are too limited to make this seamlessly.
However you can try to come close with the NULLObect I've describben.
class property
{
public function __get($name)
{
isset($this->$name) || $this->$name = new NULLObject();
return $this->$name;
}
}
class NULLObject extends Property {};
Take care that this might not be exactly what you're looking for. If it does not matches your need, it's highjly likely that PHP is the wrong language you use for programming. Use some language that has better features of member overriding than PHP has.
Related Question:
Working with __get() by reference
You can can return new property instead of null
public function __get($name)
{
return (isset($this->$name)) ? $this->$name : new property();
}
Yes, I know it's been 4 years ago, but I had a similar problem this week, and while I trying to solve it, I found this thread. So, here is my solution:
class NoneWrapper
{
private $data;
public function __construct($object)
{
$this->data = $object;
}
public function __get(string $name)
{
return property_exists($this->data, $name)
? new NoneWrapper($this->data->$name)
: new None;
}
public function __call($name, $arguments)
{
if (is_object($this->data)) {
return (property_exists($this->data, $name))
? $this->data->$name
: null;
} else {
return null;
}
}
public function __invoke()
{
return $this->data;
}
}
class None
{
public function __get(string $name) {
return new None;
}
public function __call($name, $arguments)
{
return null;
}
public function __invoke()
{
return null;
}
}
$object = new NoneWrapper(
json_decode(
json_encode([
'foo' => [
'bar' => [
'first' => 1,
'second' => 2,
'third' => 3,
'fourth' => 4,
],
]
])
)
);
var_dump($object->foo->bar->baz()); // null
var_dump($object->foo->bar->third()); // 3

Categories