Set object properties at constructor call time (PHP) - 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);

Related

php add method from object class to another object class

i have a object like this:
CORE::$ObjClassInstABS['ABS']['DATA']
that contains an array of Class->Method:
array (
'DATA' =>
array (
'USERDATAMANAGER' =>
Class_UserdataManager::__set_state(array(
)),
'PRODDATAMANAGER' =>
Class_ProddataManager::__set_state(array(
)),
),
)
i create a new object, of type class Like this:
CORE::$ObjClassInstABS['ABS']['ABSDATAMANAGER'] = new class;
i cant but need pass all the methods of the first object, ignoring the class of origin to the class i create on fly, and that allows me to execute the functions from the class declared on the fly.
does this exist in php 7.0 or is there any way to achieve this reach??
It would be like cloning the methods of several classes to a single and new class.
Answer for #Damian Dziaduch comments
the piece of code that i used to Dynamically Instance all class file from a directory is this, and populate the first object with instance of class:
CORE::$ObjClassInstABS['ABS']['ABSDATAMANAGER']= new class;
foreach (CORE::$ObjClassABS['DATA'] as $key => $name) {
if (strpos($name, 'class.') !== false) {
$name = basename($name);
$name = preg_replace('#\.php#', '', $name);
$names = explode(".", $name);
foreach ($names as $key => $namesr) {
$names[$key] = ucfirst(strtolower($namesr));
}
$name = implode('_', $names);
$NamesClass = $name . 'Manager';
$InstanceClass = strtoupper(preg_replace('#\Class_#', '', $NamesClass));
CORE::$ObjClassInstABS['ABS']['DATA'][$InstanceClass] = $this->$InstanceClass = new $NamesClass();
}
}
the result of it is the Array printed at start of the post CORE::$ObjClassInstABS['ABS']['DATA'] .
if you see at start of foreach i have the new class declaration to use, in loop, how can i populate CORE::$ObjClassInstABS['ABS']['ABSDATAMANAGER'] in the loop, it with all methods of the first object instance, and make it executables?
that i whant (not work):
foreach ( CORE::$ObjClassInstABS['ABS']['DATA'] as $key => $value ) {
CORE::$ObjClassInstABS['ABS']['ABSDATAMANAGER'] .= Clone($value);
}
$value represent where is storing the methods:
::__set_state(array()),
As requested.
Not sure whether this will fill you requirements... The question is whether you are able to overwrite the CORE::$ObjClassInstABS
<?php
CORE::$ObjClassInstABS = new class extends \ArrayIterator {
private $container = [];
public function __construct(array $container)
{
$this->container = [
'ABS' => [
'DATA' => [
'USERDATAMANAGER' => new class {},
'PRODDATAMANAGER' => new class {},
],
],
];
}
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}
public function offsetGet($offset)
{
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}
};

How to iterate over array of objects with private properties in Mustache properly?

Example of mustache template:
{{#entites}}
{{title}}
{{/entities}}
Rendered by:
$m = new Mustache_Engine(
['loader' => new Mustache_Loader_FilesystemLoader('../views')]
);
echo $m->render('index', $data);
Basic nested array.
$data = [
'entities' => [
[
'title' => 'title value',
'url' => 'url value',
]
]
];
This is rendered properly in template.
Array of objects of class:
class Entity
{
private $title;
private $url;
//setter & getters
public function __get($name)
{
return $this->$name;
}
}
Mustache argument:
$data = [
'entities' => [
$instance1
]
];
In this case not working - output is empty (no values from properties)
Instead of magic methods, why don't you use a function like this in the class
public function toArray()
{
$vars = [];
foreach($this as $varName => $varValue) {
$vars[$varName] = $varValue;
}
return $vars;
}
then call that function to grab the variables as array
$data = [
'entities' => $instance1->toArray()
];
You can make a use of ArrayAccess Interface, to be able to access your private properties as follow:
class Foo implements ArrayAccess {
private $x = 'hello';
public $y = 'world';
public function offsetExists ($offset) {}
public function offsetGet ($offset) {
return $this->$offset;
}
public function offsetSet ($offset, $value) {}
public function offsetUnset ($offset) {}
}
$a = new Foo;
print_r($a); // Print: hello
Of course this is a trivial example, you need to add more business logic for the rest of the inherited methods.

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.

Laravel late static binding as static::whereIn

Ok, I read and feel I have some understandings about PHP late static binding for methods and variables. But from line 28 in this code on Laravel 5, it uses with whereIn which is a Laravel Collection method. I don't understand what's going on here, static::whereIn(). Where is the collection so that you can use whereIn().
/**
* Add any tags needed from the list
*
* #param array $tags List of tags to check/add
*/
public static function addNeededTags(array $tags)
{
if (count($tags) === 0) {
return;
}
$found = static::whereIn('tag', $tags)->lists('tag')->all();
foreach (array_diff($tags, $found) as $tag) {
static::create([
'tag' => $tag,
'title' => $tag,
'subtitle' => 'Subtitle for '.$tag,
'page_image' => '',
'meta_description' => '',
'reverse_direction' => false,
]);
}
}
An example from php.net:
class a
{
static protected $test = "class a";
public function static_test()
{
echo static::$test; // Results class b
echo self::$test; // Results class a
}
}
class b extends a
{
static protected $test = "class b";
}
$obj = new b();
$obj->static_test();
So static::whereIn() refers to Tag::whereIn(). Same goes for static::create()

How do I fill an object in PHP from an Array

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));

Categories