PhpStorm dynamic class method warning - php

I'm trying to get PhpStorm to stop warning me about methods for a dynamically initialized class.
Here is the code for my method:
function create_or_update_objects ($object_name, $method_callbacks = []) {
// grab duplicate field names used to check for existing objects
$field = new Field();
$duplicate_field_names = $field->fetch_duplicate_names();
foreach ($_SESSION['objects'] as $session_object) {
$object = new $object_name();
// set object properties
foreach ($session_object as $key => $value) {
if (property_exists($object, $this->data[$key]['name'])) {
$object->{$this->data[$key]['name']} = $value;
}
else {
$object->fields[$this->data[$key]['name']] = $value;
}
}
// perform object method callbacks
foreach ($method_callbacks as $method_callback) {
$object->$method_callback();
}
if ($object->exists_by_duplicate_fields($duplicate_field_names)) {
// object already exists, update by data field names
$object->set_id_by_duplicate_fields($duplicate_field_names);
$object->update_by_data_names($this->data);
}
else {
// object does not exist, create it
$object->create();
}
}
}
PhpStorm is giving me warnings about the following methods not found:
exists_by_duplicate_fields
set_id_by_duplicate_fields
update_by_data_names
create
I am aware that I can use /** #noinspection PhpUndefinedMethodInspection */ above every single line - but I'm wondering if there is a better way of doing this?

What I would do:
Create an abstract class.
Add missing methods in your abstract class. They should be abstract or put real implementation with a correct return.
Above line $object = new $object_name(); add this PHPDoc instruction:
/** #var PARENT_CLASS $object */ - just replace PARENT_CLASS with a real class
Example:
abstract class MyParent {
public $fields;
abstract public function exists_by_duplicate_fields($duplicate_field_names);
abstract public function set_id_by_duplicate_fields($duplicate_field_names);
abstract public function update_by_data_names($data);
abstract public function create();
}
And then somewhere in your code:
/** #var MyParent $object */
$object = new $object_name();
Notes:
You can also add /** #var Field[] */ above public $fields; It should help PhpStorm to understand how to treat $fields member every time, when you use it.
It would be good if all that classes with names $object_name could actually be inherited from a PARENT_CLASS.
You can replace abstract methods with implementation if needed.

Related

How do I iterate through a child's private fields?

After reading this question
One idea for my problem was to implement the method in the child classes like:
class Child {
private $childField = "Want to see";
public $pubChildField = "Will see";
public function method()
{
$assoc = []; // a associative array
foreach ($this as $field => $value) {
$assoc[$field] = doStuff($value);
}
return $assoc;
}
}
var_dump((new Child())->jsonSerialize());
But for this I'd have to copycat the code in each child class. For readability, I want to refactor it into an (already existing) parentClass similar to:
abstract class parentClass {
public function method()
{
$assoc = []; // a associative array
foreach ($this as $field => $value) {
$assoc[$field] = doStuff($value);
}
return $assoc;
}
}
class Child extends parentClass {
private $childField;
public $pubChildField;
}
var_dump((new Child())->jsonSerialize());
Both foreach ($this as $k=>$v) and get_object_vars() will get the public fields from the instance. I need to get it's private fields thou, for serialization.
Edit 1: Typo abstract method - what was i thinking
Edit 2: Clarified the example
Edit 3: Reformulated the question since there seemed to be misconception
Abstract class is about abstract methods, not inherited. I assume these
are not relevant to the problem (otherwise make it ordinary default/base class).
private limits visibility to concrete class - use protected
instead.
It seems to me that you need only code reuse. You might reach for traits in this casse - traits becoming part of a class, and you can iterate private fields.
interface ArrayData {
public function toArray(): array;
}
trait ToArray {
public function toArray(): array {
$assoc = []; // a associative array
foreach ($this as $field => $value) {
$assoc[$field] = $value;
}
return $assoc;
}
}
class Foo implements ArrayData {
use ToArray;
private $childField = "Want to see";
public $pubChildField = "Will see";
}
$foo = new Foo();
var_dump($foo->toArray());
The correct solution is to declare the base class implements the JsonSerializable interface and implement the JsonSerializable::jsonSerialize() method in all children classes.
You can let it unimplemented in the base class to force all the children classes implement it or you can provide a default implementation (return array();) in the base class for the children classes that do not need to be serialized.
The implementation in each class should not iterate over the list of object properties. Usually, not all object properties must be serialized and the serializable properties are not born equal.
abstract class parentClass implements JsonSerializable {
// You can remove this default implementation
// to force all children class implement the method
public function jsonSerialize()
{
return array();
}
}
class childClass extends parentClass {
private $childField;
public $pubChildField;
public function jsonSerialize()
{
return array(
'field1' => $this->childField,
'field2' => base64_encode($pubChildField),
// etc ...
);
}
}
var_dump((new Child())->jsonSerialize());
Try to write code that is easier to read and understand. You will thank yourself later.

Regulating write access to object properties based on context

class SomeObject {
protected $foo,
$bar;
protected $context;
public function __construct($context) {
$this->context = $context;
}
public function setFoo($val) {
if ($this->context == 'public') {
throw new \Exception('It is impossible to modify foo property in public context!');
}
$this->foo = $val;
}
public function setBar($val) {
if ($this->context == 'api') {
throw new \Exception('It is impossible to modify bar property in API context!');
}
$this->bar = $val;
}
}
As you can see from this piece of "code" - object restricts setters depending on context value. This code is really hard to maintain. How can we rewrite it to make it beautiful and easy maintainable?
My thoughts are:
Make $context an object(s) implementing interface
isAllowed($object, $propertyName).
After making $context an object we have to thing about how can we store "restrictions" in $context object taking in mind there are a lot of different objects similar to SomeObject.
In every setter I should check $this->context->isAllowed($this, 'foo') - it looks not good. So, probably we want to add some "proxy" over SomeObject?
Passing $context to constructor also seems rather ugly for me.
What's your ideas about it?
Just two general observations:
You may want to segregate your classes into two parts: an immutable base class and a mutable extension:
class Foo {
protected $bar, $baz;
}
class MutableFoo extends Foo {
public function setBar($bar) {
$this->bar = $bar;
}
..
}
This easily solves the problem when the context is defined at object instantiation time and won't ever change. Instead of instantiating with a different context which determines the mutability, you simply instantiate a mutable or immutable version of the class.
If you still need more runtime checks, maybe simply using assertions is the best way to simplify the code:
public function setBar($bar) {
$this->assertCanSet('bar');
$this->bar = $bar;
}
protected function assertCanSet($property) {
if (!/* can set $property */) {
throw new Exception("Cannot set property $property");
}
}
Maybe on the construct, fill a list of restricted methods.
so, for instance :
class SomeObject {
protected $foo,
$bar;
protected $context;
protected $restrictedMethods;
public function __construct($context) {
$this->context = $context;
if($this->context == 'public') {
$this->restrictedMethods['setFoo'] = true;
} else if ($this->context == 'api') {
$this->restrictedMethods['setBar'] = true;
}
}
public function setFoo($val) {
if ($this->isRestricted('setFoo')) {
throw new \Exception('It is impossible to modify foo property in '.$this->context.' context!');
}
$this->foo = $val;
}
public function setBar($val) {
if ($this->isRestricted('setFoo')) {
throw new \Exception('It is impossible to modify bar property in '.$this->context.' context!');
}
$this->bar = $val;
}
protected function isRestricted($methodName) {
return array_key_exists($methodName, $this->restrictedMethods);
}
}
If you are trying to write good OOP, then "Interface Segregation" from the SOLID principle may be useful to you.
interface IBase
{
public function doMethod1();
public function doMethod2();
public function doMethod3();
}
interface IFoo extends IBase
{
public function setFoo($val);
}
interface IBar extends IBase
{
public function setBar($val);
}
function doWork(IBase $obj, $val)
{
$obj->doMethod1();
$obj->doMethod2();
$obj->doMethod3();
if ($obj instanceof IFoo) {
$obj->setFoo($val);
}
if ($obj instanceof IBar) {
$obj->setBar($val);
}
}
I doubt this example is exactly what you need, but I will use it to explain the basic idea.
A class should only have a "Single Responsibility". What that responsibility encompasses can vary however, so in general it is best to limit a class's functionality to a single area of concern as best you can.
If you want to follow "Liskov substitution", then throwing exceptions like that in your functions simply because the "context" was irrelevant, violates this principle.
Enter "Interface segregation":
By implementing an interface, you are (to a certain extent) guaranteeing to the caller of the implemented methods, that those methods will work. By excluding them, you are telling the caller that those methods don't exist.
In the example, the doWork function expects an instance of IBase, and safely calls the methods of that interface. After that, it runs introspection of the object to determine if other "applicable" methods are available.
The goal behind interface segregation is to limit the amount of unneeded features a class is forced to implement, so for you, if the context is public, it shouldn't need the setFoo method.
A clean solution would be to have an ObjectFactory class that creates different objects based on a $context parameter, and having two separate classes (with a common base class) that allows writing to the appropriate properties.
Please find below a possible implementation for your schema:
/**
* Base class that allows subclasses to define which properties are
* writable via setters. Subclasses must not add public setters,
* otherwise the mechanism within this class will not work; subclasses
* can add protected setters, though
*/
class PropertyRestricter {
// only properties listed here are writable
// to be initialised by subclasses
protected $writableProperties;
public function __construct() {
// default implementation allows no writable properties
$this->writableProperties = array();
}
public function __call($method, $arguments) {
$matches = false;
// check if a setter was called, extract the property name
// there needs to be at least one argument to the setter
if(count($arguments) && preg_match('/^set([A-Z][a-zA-Z0-9_]+)$/',$matches)) {
$propName = $matches[1];
$propName[0] = strtolower($propName[0]);
// update the property with the given value
// or throw an exception if the property is not writable
if(is_array($this->writableProperties) && in_array($propName, $this->writableProperties)) {
$this->{$propName} = $arguments[0];
} else {
throw new Exception(get_class() . ": $propName is not writable");
}
} else {
// not a setter, nor a public method
// maybe display a user error
}
}
}
/**
* Common properties for the actual classes
*/
class BaseObject extends PropertyRestricter {
protected $foo, $bar;
}
class PublicObject extends BaseObject {
public function __construct() {
parent::__construct();
$this->writableProperties = array('foo');
}
}
class APIObject extends BaseObject {
public function __construct() {
parent::__construct();
$this->writableProperties = array('bar');
}
}
class ObjectFactory {
public function createObject($context) {
switch($context) {
case 'public': return new PublicObject();
case 'api': return new APIObject();
default: return null;
}
}
}
The root of the objects is the PropertyRestricter class that allows subclasses to define which properties are writable. It makes use of the magic method __call() in order to be able to intercept setter calls and to validate the attempt to write to the property. However please note that this works only if subclasses don't add public setters for their properties.
The next level is the BaseObject class, which only defines the two properties, in order to reduce code redundancy.
The last level contains the two classes that get instantiated by the ObjectFactory: PublicObject, 'APIObject. These classes simply initialise thewritablePropertiesarray, as the rest of the work is done by thePropertyRestricter` class.
This is also a scalable solution, as it allows adding as many properties and subclasses as needed, each subclass defining its property writing rules.
Also the property update within the __call() method can be customised, I implemented it in the simplest way by directly setting the property. Actual setters can be used in subclasses and __call() can be updated to call the setters, with the mention that the setters need to be protected in order for the mechanism to work.

Changing object right before PHP serialises it

I have the following class tree:
class A /* Base class */
{
private/protected/public $state
}
class B extends A /* Auto generated class, not to be modified */
{
private $v
public function getV() { return $this->v; }
public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
There is only one class A. There are multiple classes like class B, and all of those classes will have a subclass like C. Class B gets auto-generated and should not be modified.
I am storing objects of type(s) C in the session. What I want to do is to store some state information in every instance, just before PHP gets it serialised, and that will do something with it when it's unserialised. I want all this to be implemented in class A.
Considering, I need to use either __sleep() or Serializable interface. Using __sleep is out of the question, because of what the PHP manual says:
It is not possible for __sleep() to return names of private properties in parent classes. Doing this will result in an E_NOTICE level error. Instead you may use the Serializable interface.
Meaning that if I sleep an instance of class C, I'll loose the private variables declared in B. So I want to use Serializable, but for some reason, I simply can't get it to do what I want.
In essence, I would like the object to be serialised just as if I didn't implement any serialisation stuff myself, I just want to add information to $state right before it happens. I've tried covering all data with ReflectionObject->getProperties(), but I can't seem to find the right way to fetch and set the private values in class B to be serialised and unserialised.
How do I do this?
You can do this using the Reflection classes. You'll have to get the properties of the class itself and each of it's parent classes. Getting and setting the property values can be done using ReflectionProperty's getValue and setValue methods, combined with setAccessible to get access to private and protected properties. Combining those, I came up with the following code:
<?php
class A implements Serializable /* Base class */
{
protected $state;
public function serialize()
{
$this->state = "something";
return serialize($this->_getState());
}
public function unserialize($data)
{
$this->_setState(unserialize($data));
}
protected function _getState()
{
$reflClass = new ReflectionClass(get_class($this));
$values = array();
while ($reflClass != null)
{
foreach ($reflClass->getProperties() as $property)
{
if ($property->getDeclaringClass() == $reflClass)
{
$property->setAccessible(true);
$values[] = array($reflClass->getName(), $property->getName(), $property->getValue($this));
}
}
$reflClass = $reflClass->getParentClass();
}
return $values;
}
protected function _setState($values)
{
foreach ($values as $_)
{
list($className, $propertyName, $propertyValue) = $_;
$property = new ReflectionProperty($className, $propertyName);
$property->setAccessible(true);
$property->setValue($this, $propertyValue);
}
}
}
class B extends A /* Auto generated class, not to be modified */
{
private $v;
public function getV() { return $this->v; }
public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
$instance = new C();
$instance->setV("value");
$s = serialize($instance);
$instance2 = unserialize($s);
var_dump($instance, $instance2);
Which seems to do what you want.

Better way to initialize a class variable

I have been searching for a while but I could not find the answer, what are the differences between these two ways of inicialize a variable class in PHP?:(if there are)
class MyClass
{
private $myVariable='something';
public function __construct()
{
}
}
class MyClass
{
private $myVariable;
public function __construct()
{
$this->myVariable='something';
}
}
If you want to initialize the variable with a default value inside the class, choose method 1.
If you want to initialize the variable with a outside value, pass the variable through the constructor and choose method 2.
See this scenario:
class Parent {
protected $property1; // Not set
protected $property2 = '2'; // Set to 2
public function __construct(){
$this->property1 = '1'; // Set to 1
}
} // class Parent;
class Child extends Parent {
public function __construct(){
// Child CHOOSES to call parent constructor
parent::__construct(); // optional call (what if skipped)
// but if he does not, ->property1 remains unset!
}
} // class Child;
This is a difference between the two calls. parent::__construct() is optional for child classes that inherit from a parent. So:
if you have scalar (as in is_scalar()) properties that need to be preset, do it in the class definition to be sure that they exist in child classes too.
if you properties that depend on arguments or are optional, put them in the constructor.
It all depends on how you design your code's functionality.
There's no right of wrong here, it's only what's right for you.
I like to do it somewhat like the second way to facilitate lazy-loading. Simple values I will set when declaring the member variable.
class WidgetCategory
{
/** #var array */
private $items;
/**
*
* #return array
*/
public function getItems()
{
if (is_null($this->items)) {
$this->items = array();
/* build item array */
}
return $this->items;
}
}
You can only use constant values if you don't choose to initialize the variable in the constructor. Here a little example:
define('MY_CONSTANT', 'value');
class MyClass
{
// these will work
private $myVariable = 'constant value';
private $constant = MY_CONSTANT;
private $array = Array('value1', 'value2');
// the following won't work
private $myOtherVariable = new stdClass();
private $number = 1 + 2;
private $static_method = self::someStaticMethod();
public function __construct($param1 = '')
{
$this->myVariable = $param1;
// in here you're not limited
$this->myOtherVariable = new stdClass();
$this->number = 1 + 2;
$this->static_method = self::someStaticMethod();
}
}
Take a look at this Manual Page to see what values are allowed to be directly assinged to properties: http://php.net/manual/en/language.oop5.properties.php
There may be more differences ...

in one class run many class, PHP OOP

i have class "User_registration" and in this class i need use many class: "Location", "Links", "Mail", "Module".
i create include all class in file:
include 'class/location.php';
include 'class/links.php';
include 'class/mail.php';
include 'class/modules.php';
Now create "User_registration" class.
<?php
class User_registration{
public function insert_to_db($name, $country_code, $home_page)
{
//work with data
return id;
}
public function show_info($id)
{
//work with data
}
}
$reg_u = new User_registration;
$res = $reg_u->insert_to_db($name, $country_code, $home_page);
if($res){
$reg_u->show_info($res);
}
?>
I need in method "insert_to_db" run class: "Location", "Links", "Mail" methods
and in "show_info" run some methods of "Location", "Links", "Module" class.
How? How in one class run another class (no't one)
Thanks for help ;)
There are a few ways to do this. If you have only a couple objects that another class needs to utilize, use dependency injection; Pass each object as an argument into a class's constructor and store those objects as a class property.
If only a single method needs the object, pass the object as an argument of the method. I discourage this approach though, because I feel it hinders expandability/code-cleanliness in the long run.
If you have many objects that are needed in several classes, I recommend a registry that you inject into a class's constructor. The registry is a singleton (it holds a single instance of each object you need to share). In the class that needs to utilize a shared object, you might call $this->registry->get('Some_Shared_Object')->doSomething().
Dependency Injection (at the constructor)
class Foo {
protected $dependency1;
protected $dependency2;
protected $dependency3;
public function __construct($dependency1, $dependency2, $dependency3) {
$this->dependency1 = $dependency1;
$this->dependency2 = $dependency2;
$this->dependency3 = $dependency3;
}
public function foo() {
$this->dependency1->doSomething();
}
}
$foo = new Foo($dependency1, $dependency2, $dependency3);
$foo->foo();
Dependency Injection (at the method, not recommended)
class Foo {
public function foo($dependency1) {
$dependency1->doSomething();
}
}
$foo = new Foo();
$foo->foo($dependency1);
Dependency Injection using a Registry
class Registry {
var $data = array();
function __get($key) {
return $this->get($key);
}
function __set($key, $value) {
$this->set($key, $value);
}
/**
* Retrieve a resource from the registry.
*
* #param string
* #return mixed|null
*/
function get($key) {
return isset($this->data[$key]) ? $this->data[$key] : NULL;
}
/**
* Store a resource in the registry.
*
* #param string
* #param mixed
*/
function set($key, &$value) {
$this->data[$key] = $value;
}
/**
* Check if a resource exists in the registry.
*
* #param string
* #return boolean
*/
function has($key) {
return isset($this->data[$key]);
}
}
class Foo {
protected $registry;
public function __construct($registry) {
$this->registry = $registry;
}
public function foo() {
$this->registry->dependency1->doSomething();
}
}
$dependency1 = new Dependency1();
$registry = new Registry();
$registry->set('dependency1', $dependency1);
$foo = new Foo($registry);
$foo->foo();
As good practice I always use an include_once/require_once when I call a class from a class. That way I know no matter where a use a class its references are taken care of and don't over lap.
Well initialize an instance of each and call your methods from there. Don't be afraid of static references.

Categories