Create dynamic class variables in yii2 - php

I have an ActiveRecord class where I need to have some kind of dynamic variables in it.
The dynamic variable should be created for each existing variable. For example if I have a variable $test in my class, then it should also has $testDyn.
Example:
class User extends ActiveRecord
{
public $name;
public $age;
public $address;
/**
* dynamic vars
*/
public $nameDyn;
public $ageDyn;
public $addressDyn;
public function tableName()
{
return 'user';
}
...
}
There are ActiveRecords which have many variables and it would not be the best idea to do and manually create those dynamic vars for each variables. Are there any easy way / behavior which can do this?

You can create custom behavior for these purposes and attach for every needed model.
As for properties, you can explicitly specify it via parameter through behavior config, or use something like this:
$reflect = new ReflectionClass(new YourClass());
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
More info and examples are available here.
You can additionally use some naming convention.
For dynamic properties you can override __get() magic method in behavior. See here.
Yii2 example (taken from yii2tech/ar-linkmany behavior by Paul Klimov, one of the framework contributors):
/**
* PHP getter magic method.
* This method is overridden so that relation attribute can be accessed like property.
*
* #param string $name property name
* #throws UnknownPropertyException if the property is not defined
* #return mixed property value
*/
public function __get($name)
{
try {
return parent::__get($name);
} catch (UnknownPropertyException $exception) {
if ($name === $this->relationReferenceAttribute) {
return $this->getRelationReferenceAttributeValue();
}
throw $exception;
}
}
To understand more about behaviors concept, please refer to according official docs section Behaviors.

Related

PHPUnit - mock method of class use created instance

I want to mock a method foo in a class but leave method bar as it is:
<?php
class MyClass
{
protected $dep1;
protected $dep2;
protected $dep3;
protected $dep4;
/**
* Test constructor.
* #param $dep1
* #param $dep2
* #param $dep3
* #param $dep4
*/
public function __construct($dep1, $dep2, $dep3, $dep4)
{
$this->dep1 = $dep1;
$this->dep2 = $dep2;
$this->dep3 = $dep3;
$this->dep4 = $dep4;
parent::__construct();
}
public function foo()
{
return "foo";
}
public function bar()
{
return "bar";
}
}
However, MyClass is instantiated via a factory which retrieves the dependencies ($dep1, $dep2, ...) and inject them directly in to the constructor of MyClass.
So I want to use that Factory and instantiate a MyClass-object (otherwise the complex instantiation has to be coded in the test case also).
In short I want to know if there is another solution than:
class TestMyClass extends \PHPUnit\Framework\TestCase {
public function setUp()
{
// complicated way to retrieve $dep1 to $dep4
$mock = $this->getMockBuilder(MyClass::class)->setMethods(['foo'])->setConstructorArgs([$dep1, $dep2, $dep3, $dep4])->getMock();
$mock->expects($this->any())->method('foo')->willReturn(false);
}
}
Is it possible to somehow create proxy to an already created instance of MyClass, which just overrides the foo method? Is there another way (without runkit pecl extension) to mock an method of an existing/instantiated object?
I know PHPUnit has some Proxy-related methods, but I couldn't find any documentation / example of usage of them, so I am not sure if they even could be used to solve my problem.

Why can't I use public properties in Doctrine entities?

Say I have a very simple CRUD system in PHP to manage a database. Say it holds a list of products. Using Doctrine ORM, I'd like to query the database and view/edit/add records. According to the Getting Started manual,
When creating entity classes, all of the fields should be protected or
private (not public), with getter and setter methods for each one
(except $id). The use of mutators allows Doctrine to hook into calls
which manipulate the entities in ways that it could not if you just
directly set the values with entity#field = foo;
This is the sample provided:
// src/Product.php
class Product
{
/**
* #var int
*/
protected $id;
/**
* #var string
*/
protected $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
// Recording a new title
$product->setName("My new name");
$db->persist($product);
$db->flush();
// Echoing the title
echo $product->getName();
However, this seems overly complicated. Say I don't have a need to hook into calls to manipulate entities, as explained in the manual. I can make this code much shorter and do this:
// src/Product.php
class Product
{
/**
* #var int
*/
public $id;
/**
* #var string
*/
public $name;
}
This would allow things like this:
$product = $db->getRepository('Product')->find(1);
// Recording a new name
$product->name = "My new title";
$db->persist($product);
$db->flush();
// Echoing the title
echo $product->name;
The advantages are:
Always using the exact same name
No extra setters and getters in the entity class
What is the disadvantage of using this? Am I taking certain risks here?
Say I don't have a need to hook into calls to manipulate entities, as explained in the manual.
You do not need to hook into these calls, but Doctrine does. Doctrine internally overrides these getters and setters to implement an ORM as transparently as possible.
Yes, it is overly complicated, but that is the PHP language at fault and not Doctrine. There is nothing inherently wrong with using public properties, it is just that the PHP language does not allow creating custom getters/setters the way some other languages do (such as Kotlin, C#).
What does Doctrine do exactly? It generates so-called Proxy objects to implement lazy loading. These objects extend your class and override certain methods.
For your code:
// src/Product.php
class Product
{
/**
* #var int
*/
protected $id;
/**
* #var string
*/
protected $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
Doctrine might generate:
class Product extends \YourNamespace\Product implements \Doctrine\ORM\Proxy\Proxy
{
// ... Lots of generated methods ...
public function getId(): int
{
if ($this->__isInitialized__ === false) {
return (int) parent::getId();
}
$this->__initializer__ && $this->__initializer__->__invoke($this, 'getId', []);
return parent::getId();
}
public function getName(): string
{
$this->__initializer__ && $this->__initializer__->__invoke($this, 'getName', []);
return parent::getName();
}
// ... More generated methods ...
}
(What this does exactly is not the point, the point is that Doctrine MUST be able to override getters and setters to implement an ORM)
To see for yourself, inspect the generated Proxy objects in the proxy directory of Doctrine. See the Advanced Configuration section for more info on configuring the proxy directory. Also see Working with Objects for more info on Proxy objects and how Doctrine utilizes them.
Why not use public entity classes in Doctrine?
Actually what you are asking is:
Where does this getters/setters come from?
It comes from the Encapsulation in OOP.
so, what is encapsulation?
It’s the way we define the visibility of our properties and methods. When you’re creating classes, you have to ask yourself what properties and methods can be accessed outside of the class.
Let’s say we had a property named id. If a class extends your class, is it allowed to manipulate and access id directly? What if someone creates an instances of your class? Are they allowed to manipulate and access id?
NO! They should not be able to reach the id attribute directly.
Why not?
Example: What happens if you only accept Invoice Ids which start with "IX" and other classes have access to your class and they set the invoice_id directly?
What happens if they accidentally set the invoice_id to "XX12345"?
of course, you cannot do anything (like validating the input), because you are using public properties and no setters.
but if you had a setter method in your class, you could just do
private $invoiceId;
public function setInvoiceId($invoiceId)
{
if (is_null($invoiceId) || preg_match("#^IX(.*)$#i", $invoiceId) === 0)
return false;
$this->invoiceId = $invoiceId;
return $this;
}
I hope it was clear enought. I will try to extend the answer

How can I define PDO in SLIM Framework so that PhpStorm doesn't throw 'method not found in class' warnings?

So I stick PDO into my SLIM using this:
$container['dbh'] = function($container) {
$config = $container->get('settings')['pdo'];
$dsn = "{$config['engine']}:host={$config['host']};dbname={$config['database']};charset={$config['charset']}";
$username = $config['username'];
$password = $config['password'];
return new PDO($dsn, $username, $password, $config['options']);
};
However, every time I use $this->dbh->execute() (or some other PDO method), PhpStorm warns me method 'execute' not found in class
Realistically it doesn't make a difference but I'd like my PhpStorm to stop warning me about things that it doesn't need to.
This is mostly an answer adding to #rickdenhaan's comment.
I noticed that you're using $this, which implies you have a class somewhere.
You can typehint dynamic/fake properties in a class like so:
/**
* #property PDO $dbh
*/
class MyApp {
}
For more help, read the PHPDoc documentation, eg here.
In some situations, you might not be able to influence the original class being instantiated. In that case, you can have a stub file; basically a class used only for type hinting:
// Let's assume you cannot modify this class.
class App {}
// This is the stub class, we declare it as abstract to avoid instantiation by mistake.
// We make it extend `App` to get metadata from there.
abstract class AppStub extends App {
/** #var PDO */
public $dbh;
}
// ...later on in your code..
/** #var AppStub $this */
// tada!
$this->dbh->execute();
Controller classes approach
Your main app + routing:
$app = new \Slim\App();
$app->get('/', \YourController::class . ':home');
$app->get('/contact', \YourController::class . ':contact');
And your controller:
class YourController
{
protected $container;
// constructor receives container instance
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
public function home($request, $response, $args) {
$this->getDb()->execute(); // no IDE errors anymore!
return $response;
}
public function contact($request, $response, $args) {
return $response;
}
/**
* #return PDO
*/
protected function getDb()
{
return $this->container->get('dbh');
}
}
If your class is in a namespace, you should use \PDO to indicate that you are referring to a class in the root namespace.
If PhpStorm has problems with identifying variable/object, so for example it has warning 'Method some_method not found in...' for example for code like:
$my_object->some_method();
then you can help PhpStorm to identify class by adding variable type definition in comment like this:
/**
* #var \Some\Class $my_object
*/
$my_object->some_method();
General example. Add this comment before using $my_object variable (for example at the beggining of method were you will use variable $my_object, or before first usage of this variable):
/**
* #var \Some\Class $my_object
*/
and now every time you will use this variable in method, PhpStorm will know that this variable is an instance of \Some\Class, so warnings will disappear in following lines, for every usage of this variable. For example warnings will not show for this kind of usage:
/**
* #var \Some\Class $my_object
*/
...
$my_object->some_method();
...
$my_object->some_method1();
...
$my_object->some_method2();
...
$my_object->some_method();

PHP dependency injection - difference between a container, a controller or a factory

I still cannot tell the difference between a container, a controller or a factory. For instance, the code below, should it be considered as a container, a controller or a factory?
How should it be like if it is a container in this case?
namespace ioc
{
class LoggerContainer
{
use \Snippets;
/*
* Set props.
*/
protected $Database;
public $Logger;
/**
* Construct data.
*/
public function __construct($Database)
{
// Set DI.
$this->Database = $Database;
// Run private method.
$this->getLogger();
}
/**
* Set a new class (instantiate the class) as the return result.
*/
private function getLogger()
{
$signature = AUTHENTICATED_USER_SIGNATURE;
$this->Logger = new \core\model\Logger($this->Database);
if($_REQUEST['url'] === 'backoffice')
{
return $this->Logger->setLogger($signature)->addCategory()->addSomething();
}
else
{
return $this->Logger->setLogger($signature)->removeSomething();
}
}
}
}
This is a container because it is an object that holds another object that is being accessed, i.e. a Logger in this case.
A controller is a term that usually refers to the C of the MVC and it is what controls the model and the view
While a factory method refers to a routines that returns the correct specialization of an interface/abstract class
Note that this three terms refer to very different things

PHP Set variable in __construct()

Is it normal to not defined variable in class properties, but indicate at once in constructor ?
I just learn OOP, and found that example:
class Foo {
public function __construct($name) {
$this->_name = $name;
}
}
and it works, i can use this variable
$objFoo = new Foo('Fido');
echo $objFoo->_name; //output 'Fido'
But in class properties, there has no defined variable $_name, like public $_name in start of class.
So if we dont define it directly, it will be created automatically throught constructor ? And is it real to set scope for variable that way, protected, private ?
In PHP, this is called "object overloading", you can add/set new properties on (almost) any instance on the fly. However, you shouldn't do this. These properties make your code more error prone, less maintainable and overloaded properties that are added are slower: details here.
Pre-declaring properties allows for better documentation, your IDE's auto-completion function is actually worth something then, and you don't get in the habit of relying on something that doesn't always work. I tend to write my data models along these lines:
class DataModel
{
/**
* #var int
*/
protected $id = null;
/**
* #var string
*/
protected $name = null;
/**
* #var \DateTime
*/
protected $birthday = null;
public function __construct(array $data)
{}//set values
public function __set($name, $value)
{
throw new \BadMethodCallException('Do not attempt overloading, use getters and setter');
}
}
I define getters and setters for all of the properties, to ensure type safety, and validation can be done. I expressly disable overloading because of the risk of typo's, and difficult debugging. I often find myself disabling __sleep, __serialize and all those magic methods for pretty much the same reason.
Creating object members on the fly, meaning just assigning a value to it without declaring the member, is not limited to the constructor method. It can be done from inside of every method in the class or even from outside of the class, like this:
$c = new stdClass();
$c->foo = "bar";
While there are still a few use cases for this, I suggest to declare every class member explicitly in the head of the class. Also you will want in most cases to declare class members as protected or even private which requires an explicit declaration anyway.

Categories