Pass options to ClassMethodsHydrator in zend-expressive-hal config - php

I use zend-expressive-hal (v3) and have the following config written for the delivery of my User class:
return [
[
'__class__' => RouteBasedResourceMetadata::class,
'resource_class' => Handler\User::class,
'route' => 'users',
'extractor' => ClassMethodsHydrator::class,
],
];
This works without any problems. What I have noticed, however, is that the keys are stored in the generated JSON with underscores, while in my User class the methods are written camel case. How can I supplement my above configuration to pass options to the ClassMethodsHydrator class, e.g. underscoreSeparatedKeys = false?

Aparently, I am not using the latest version of zend hydrator, so I don't have the Zend\Hydrator\ClassMethodsHydrator class.
I built my own hydrator (I know for sure that the objects have getters and setters for each property):
class ObjectWithGetterAndSetterHydrator extends AbstractHydrator
{
public function extract($object)
{
if (!$object instanceof ApiEntityInterface) {
throw new \RuntimeException('Could not extract object. Object must be instance of ' . ApiEntityInterface::class);
}
/** #var ApiEntityInterface $object */
$properties = $object::getExportableProperties();
$data = [];
foreach ($properties as $property) {
$data[$property] = method_exists($object, 'get' . ucfirst($property)) ? $object->{'get' . ucfirst($property)}() : $object->{'is' . ucfirst($property)}();
}
return $data;
}
public function hydrate(array $data, $object)
{
foreach ($data as $key => $value) {
$object->{'set' . ucfirst($key)}($value);
}
}
}

Related

How to pass an array of setting values across multiple classes, efficiently

I have a settings array of config values that don't change and need to use these values in various classes.
$settings = ['foo' => 'bar', 'license_id' => '12345'];
Currently, I am using a static class and anytime I need a config value inside a class I do this:
$this->global_config = MyClass::get_global_config_array();
if ( $this->global_config['license_id'] ) do this . . .
Is there another way than using a global, dependency injection, or a singleton class? I make a lot of calls to MyClass::get_global_config_array() and was looking for a way to be more efficient.
FYI: I am new to OOP, but not procedural.
class Settings() {
public static function global_config_array() {
$settings = ['foo' => 'bar', 'license_id' => '12345'];
return $settings;
}
}
class Render_Setup_Page() {
public function __construct() {
$this->global_settings = Settings::global_config_array();
}
public function check_license() {
if ( $this->global_settings['license_id'] ) do this . . .
}
}
class Render_Profile_page() {
public function __construct() {
$this->global_settings = Settings::global_config_array();
}
public function user_info() {
ret 'first name: ' . $this->global_settings['foo'] );
}
}
UPDATE:
To add more content to this request. I am building a WordPress plugin that has plugin settings that are divided into three types: static, dynamic, and the theme customizer.
I merge the three into a global settings array and pass the array around to several functions. Having them in a single array it eliminates a lot of calls for data. Especially in a theme template.
Based on the suggestion from #miken32 to use trait, I have come up with the following example:
trait Global_Settings {
private array $static = [
"foo" => "bar",
"license_id" => "12345"
];
private array $dynamic = [
'plugin_build' => '73686572726965206',
'plugin_version' => '1.0.0',
'plugin_prefix' => 'xyx',
];
private array $customizer = [
'plugin_123' => 'hello',
'plugin_456' => 'there',
];
private function get_static_settings() {
return $this->static;
}
private function get_dynamic_settings() {
return $this->dynamic;
}
private function get_customizer_settings() {
return $this->customizer;
}
private function global_settings() {
return array_merge(
$this->static,
$this->dynamic,
$this->customizer,
);
}
}
class Do_Something {
use Global_Settings;
public function render_something() {
error_log( print_r($this->get_dynamic_settings(), TRUE) );
error_log( print_r($this->get_static_settings(), TRUE) );
error_log( print_r($this->get_customizer_settings(), TRUE) );
error_log( print_r($this->global_settings(), TRUE) );
// Get a specific settings array.
$output = 'Your' . $this->get_dynamic_settings['foo'] . ' license is: ' . $this->get_dynamic_settings['license_id'];
// Or get all of them at once.
extract( $this->global_settings );
$output = $plugin_123 . ' your' . $foo . ' license is: ' . $license_id . ' for version ' . $plugin_version;
return $output;
}
}
$obj = new Do_Something();
$obj->render_something();
My follow up question is how to assign the results from another class that has a settings array to the $static property in the trait Global_Settings?
class Static_Array {
public array() {
return [
"foo" => "bar",
"license_id" => "12345"
];
}
}
If these values really are fixed, you could have the classes import a trait that holds the settings and also incorporates any common functionality you need.
trait HasSettings
{
private array $settings = ["foo" => "bar", "license_id" => "12345"];
private function hasLicense(): bool
{
return !empty($this->settings["license_id"]);
}
private function getFoo(): string
{
return $this->settings["foo"] ?? "";
}
}
class Render_Setup_Page
{
use HasSettings;
public function __construct()
{
// do stuff
}
public function check_license()
{
if ( $this->hasLicense()) {
// do stuff
}
}
}
class Render_Profile_page
{
use HasSettings;
public function __construct()
{
// do stuff
}
public function user_info()
{
// don't ever echo from a class method
return 'first name: ' . $this->getFoo();
}
}
Providing methods to access these values is a good idea, but you also have access to the private array via $this->settings.

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

What is the most efficient way to get the relationship method names on a Laravel Eloquent object?

A lot of time, I've had reasons to get the names of the relationships defined on an Eloquent object. Since Laravel currently provides no way/helper to do this, I came up with the below:
public static function getRelationshipsForEloquentlModel($eloquentObject) {
$methods = self::getDirectClassMethods($eloquentObject);
$relations = [];
foreach ($methods as $method) {
try {
$reflection = new \ReflectionMethod($eloquentObject, $method);
//filter out non-eloquent relationship methods that expect parameters in
//their signature (else they blow up when they get called below without pars)
$pars = $reflection->getNumberOfParameters();
if ($pars == 0) {
$possibleRelationship = $eloquentObject->$method();
//one of the things we can use to distinctively identify an eloquent
//relationship method (BelongsTo, HasMany...etc) is to check for
//one of the public methods defined in Illuminate/Database/Eloquent/Relations/Relation.php
//(and hope that it is not discontinued/removed in future versions of Laravel :))
if (method_exists($possibleRelationship, "getEager")) {
$relationshipType = get_class($possibleRelationship);
//remove namespace
if ($pos = strrpos($relationshipType, '\\')) {
$relationshipType = substr($relationshipType, $pos + 1);
}
$relations[$method] = $relationshipType;
}
}
} catch (\Exception $ex) {
//Eloquent's save() method will throw some
//sql error because $eloquentObject may be
//an empty object like new App\User (so some NOT NULL db fields may blow up)
}
}
return $relations;
}
And the helper class getDirectClassMethods is below (courtesy of onesimus on official PHP docs comment):
public static function getDirectClassMethods($class) {
$array1 = get_class_methods($class);
if ($parent_class = get_parent_class($class)) {
$array2 = get_class_methods($parent_class);
$array3 = array_diff($array1, $array2);
} else {
$array3 = $array1;
}
return ($array3);
}
Now this whole code listing looks so cumbersome and verbose to me, at least when the desired task is such a simple one. Is there a better/faster/more efficient way of achieving this without all these verbosity?
This trait should help
namespace App;
use ErrorException;
use Illuminate\Database\Eloquent\Relations\Relation;
use ReflectionClass;
use ReflectionMethod;
trait RelationshipsTrait
{
public function relationships() {
$model = new static;
$relationships = [];
foreach((new ReflectionClass($model))->getMethods(ReflectionMethod::IS_PUBLIC) as $method)
{
if ($method->class != get_class($model) ||
!empty($method->getParameters()) ||
$method->getName() == __FUNCTION__) {
continue;
}
try {
$return = $method->invoke($model);
if ($return instanceof Relation) {
$relationships[$method->getName()] = [
'type' => (new ReflectionClass($return))->getShortName(),
'model' => (new ReflectionClass($return->getRelated()))->getName()
];
}
} catch(ErrorException $e) {}
}
return $relationships;
}
}
You should get an array of arrays, just add the trait to any models.
class Article extends Model
{
use RelationshipsTrait;
...
}
$article = new Article;
dd($article->relationships());
Should output
"example" => array:2 [▼
"type" => "BelongsTo"
"model" => "App\Example"
],
"gallery" => array:2 [▼
"type" => "MorphMany"
"model" => "App\Gallery"
]

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.

json_decode to custom class

Is it possible to decode a json string to an object other than stdClass?
Not automatically. But you can do it the old fashioned route.
$data = json_decode($json, true);
$class = new Whatever();
foreach ($data as $key => $value) $class->{$key} = $value;
Or alternatively, you could make that more automatic:
class Whatever {
public function set($data) {
foreach ($data AS $key => $value) $this->{$key} = $value;
}
}
$class = new Whatever();
$class->set($data);
Edit: getting a little fancier:
class JSONObject {
public function __construct($json = false) {
if ($json) $this->set(json_decode($json, true));
}
public function set($data) {
foreach ($data AS $key => $value) {
if (is_array($value)) {
$sub = new JSONObject;
$sub->set($value);
$value = $sub;
}
$this->{$key} = $value;
}
}
}
// These next steps aren't necessary. I'm just prepping test data.
$data = array(
"this" => "that",
"what" => "who",
"how" => "dy",
"multi" => array(
"more" => "stuff"
)
);
$jsonString = json_encode($data);
// Here's the sweetness.
$class = new JSONObject($jsonString);
print_r($class);
We built JsonMapper to map JSON objects onto our own model classes automatically. It works fine with nested/child objects.
It only relies on docblock type information for mapping, which most class properties have anyway:
<?php
$mapper = new JsonMapper();
$contactObject = $mapper->map(
json_decode(file_get_contents('http://example.org/contact.json')),
new Contact()
);
?>
You can do it - it's a kludge but totally possible. We had to do when we started storing things in couchbase.
$stdobj = json_decode($json_encoded_myClassInstance); //JSON to stdClass
$temp = serialize($stdobj); //stdClass to serialized
// Now we reach in and change the class of the serialized object
$temp = preg_replace('#^O:8:"stdClass":#','O:7:"MyClass":',$temp);
// Unserialize and walk away like nothing happend
$myClassInstance = unserialize($temp); // Presto a php Class
In our benchmarks this was way faster than trying to iterate through all the class variables.
Caveat: Won't work for nested objects other than stdClass
Edit: keep in mind the data source, it's strongly recommended that you don't do this withe untrusted data from users without a very carful analysis of the risks.
You could use Johannes Schmitt's Serializer library.
$serializer = JMS\Serializer\SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, 'MyNamespace\MyObject', 'json');
In the latest version of the JMS serializer the syntax is:
$serializer = SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, MyObject::class, 'json');
I'm surprised no one mentioned this, yet.
Use the Symfony Serializer component: https://symfony.com/doc/current/components/serializer.html
Serializing from Object to JSON:
use App\Model\Person;
$person = new Person();
$person->setName('foo');
$person->setAge(99);
$person->setSportsperson(false);
$jsonContent = $serializer->serialize($person, 'json');
// $jsonContent contains {"name":"foo","age":99,"sportsperson":false,"createdAt":null}
echo $jsonContent; // or return it in a Response
Deserializing from JSON to Object: (this example uses XML just to demonstrate the flexibility of formats)
use App\Model\Person;
$data = <<<EOF
<person>
<name>foo</name>
<age>99</age>
<sportsperson>false</sportsperson>
</person>
EOF;
$person = $serializer->deserialize($data, Person::class, 'xml');
You can do it in below way ..
<?php
class CatalogProduct
{
public $product_id;
public $sku;
public $name;
public $set;
public $type;
public $category_ids;
public $website_ids;
function __construct(array $data)
{
foreach($data as $key => $val)
{
if(property_exists(__CLASS__,$key))
{
$this->$key = $val;
}
}
}
}
?>
For more details visit
create-custom-class-in-php-from-json-or-array
You can make a wrapper for your object and make the wrapper look like it is the object itself. And it will work with multilevel objects.
<?php
class Obj
{
public $slave;
public function __get($key) {
return property_exists ( $this->slave , $key ) ? $this->slave->{$key} : null;
}
public function __construct(stdClass $slave)
{
$this->slave = $slave;
}
}
$std = json_decode('{"s3":{"s2":{"s1":777}}}');
$o = new Obj($std);
echo $o->s3->s2->s1; // you will have 777
No, this is not possible as of PHP 5.5.1.
The only thing possible is to have json_decode return associate arrays instead of the StdClass objects.
Use Reflection:
function json_decode_object(string $json, string $class)
{
$reflection = new ReflectionClass($class);
$instance = $reflection->newInstanceWithoutConstructor();
$json = json_decode($json, true);
$properties = $reflection->getProperties();
foreach ($properties as $key => $property) {
$property->setAccessible(true);
$property->setValue($instance, $json[$property->getName()]);
}
return $instance;
}
As Gordon says is not possible. But if you are looking for a way to obtain a string that can be decoded as an instance of a give class you can use serialize and unserialize instead.
class Foo
{
protected $bar = 'Hello World';
function getBar() {
return $this->bar;
}
}
$string = serialize(new Foo);
$foo = unserialize($string);
echo $foo->getBar();
I once created an abstract base class for this purpose. Let's call it JsonConvertible. It should serialize and deserialize the public members. This is possible using Reflection and late static binding.
abstract class JsonConvertible {
static function fromJson($json) {
$result = new static();
$objJson = json_decode($json);
$class = new \ReflectionClass($result);
$publicProps = $class->getProperties(\ReflectionProperty::IS_PUBLIC);
foreach ($publicProps as $prop) {
$propName = $prop->name;
if (isset($objJson->$propName) {
$prop->setValue($result, $objJson->$propName);
}
else {
$prop->setValue($result, null);
}
}
return $result;
}
function toJson() {
return json_encode($this);
}
}
class MyClass extends JsonConvertible {
public $name;
public $whatever;
}
$mine = MyClass::fromJson('{"name": "My Name", "whatever": "Whatever"}');
echo $mine->toJson();
Just from memory, so probably not flawless. You will also have to exclude static properties and may give derived classes the chance to make some properties ignored when serialized to/from json. I hope you get the idea, nonetheless.
JSON is a simple protocol to transfer data between various programming languages (and it's also a subset of JavaScript) which supports just certain types: numbers, strings, arrays/lists, objects/dicts. Objects are just key=value maps and Arrays are ordered lists.
So there is no way to express custom objects in a generic way. The solution is defining a structure where your program(s) will know that it's a custom object.
Here's an example:
{ "cls": "MyClass", fields: { "a": 123, "foo": "bar" } }
This could be used to create an instance of MyClass and set the fields a and foo to 123 and "bar".
I went ahead and implemented John Petit's answer, as a function(gist):
function json_decode_to(string $json, string $class = stdClass::class, int $depth = 512, int $options = 0)
{
$stdObj = json_decode($json, false, $depth, $options);
if ($class === stdClass::class) return $stdObj;
$count = strlen($class);
$temp = serialize($stdObj);
$temp = preg_replace("#^O:8:\"stdClass\":#", "O:$count:\"$class\":", $temp);
return unserialize($temp);
}
This worked perfectly for my use case. However Yevgeniy Afanasyev's response seems equally promising to me. It could be possible to have your class have an extra "constructor", like so:
public static function withJson(string $json) {
$instance = new static();
// Do your thing
return $instance;
}
This is also inspired by this answer.
EDIT: I have been using karriereat/json-decoder for some time now, and I have had absolutely no trouble with it. It is lightweight and very easily extensible. Here's an example of a binding I wrote to deserialize JSON into a Carbon/CarbonImmutable object.
All this here inspired me to a generic function:
function loadJSON($Obj, $json)
{
$dcod = json_decode($json);
$prop = get_object_vars ( $dcod );
foreach($prop as $key => $lock)
{
if(property_exists ( $Obj , $key ))
{
if(is_object($dcod->$key))
{
loadJSON($Obj->$key, json_encode($dcod->$key));
}
else
{
$Obj->$key = $dcod->$key;
}
}
}
}
to be called in class declaration:
class Bar{public $bar = " Boss";}
class Bas
{
public $ber ;
public $bas=" Boven";
public function __construct()
{$this->ber = new Bar;}
}
class Baz
{
public $bes ;
public $baz=" Baaz";
public function __construct()
{$this->bes = new Bas;}
}
$Bazjson = '{"bes":{"ber":{"bar":"Baas"}}}';
$Bazobj = new Baz;
loadJSON($Bazobj, $Bazjson);
var_dump($Bazobj);
This worked for me, especially for if you don't have setters or named properties in the target class
function cast($jsonstring, $class)
{
//$class is a string like 'User'
$json= json_decode($jsonstring,true); //array
$reflection = new ReflectionClass($class);
$instance = $reflection->newInstanceWithoutConstructor();
$keys = array_keys($json);
foreach ($keys as $key => $property) {
$instance->{$property} =$json[$property];
}
// print_r($instance);
return $instance;
}
Not directly, but if the class has a constructor with parameter names that match the keys in the JSON object, you can simply decode the JSON into an associative array and pass it to the constructor via the '...' (argument unpacking) operator:
<?php
class MyClass {
public function __construct(
public int $id,
public string $name,
public array $attributes,
){}
}
$json = '{"name":"foo","id":42,"attributes":{"color":"red"}}';
$object = new MyClass(...json_decode($json, true));
print_r($object);
Output:
MyClass Object
(
[id] => 42
[name] => foo
[attributes] => Array
(
[color] => red
)
)
However, in practice, there is often some additional mapping to do, especially sub-objects that need to be recursively decoded too. So usually it is better to have a static fromArray function in each class that pre-processes the json-decoded array before passing the result to the constructor:
class Part {
public function __construct(public float $weight){}
public static function fromArray(array $data): self {
return new self(...$data);
}
}
class System {
public function __construct(
public string $name,
public Part $mainPart,
public array $otherParts,
){}
public static function fromArray(array $data): self {
$data['mainPart'] = Part::fromArray($data['mainPart']);
$data['otherParts'] = array_map(Part::fromArray(...), $data['otherParts']); // php 8.1
return new self(...$data);
}
}
$json = '{"name":"foo","mainPart":{"weight":2},"otherParts":[{"weight":1}, {"weight":0.5}]}';
$object = System::fromArray(json_decode($json, true));

Categories