Class properties as Array instead of variables - php

I'm starting to work with classes in PHP.
I have been reading and I noticed PHP is all about arrays.
So I was wondering if it would be a good practice to use the class properties inside array and naming them after keys.
Like this:
private $prefix;
private $name;
public function setPrefix($p)
{
$this->prefix = $p;
}
public function getPrefix()
{
return $this->prefix;
}
public function setName($n)
{
$this->name = $n;
}
public function getName()
{
return $this->name;
}
That's the common way of doing this.
But instead do it like this:
private $data = array();
public function setData($property, $value)
{
$this->data[$property] = $value;
}
public function getData($property)
{
return $this->data[$property];
}
Would this be better than the common way? I believe that would be a generic class structure for any database table.

Would this be better than the common way?
NO. And in fact it have drawbacks.
It removes the public, protected and private encapsulation of your properties (which is in the essence of oop).
Adds a layer over every variable access. I don't really know the internals of php, but I really don't think it could be faster than native properties. (although the difference is probably absolutely irrelevant to any script)
IDE's won't be able to complete your code when accessing properties.
It can have it's uses, if your class is a container which needs to have an array of internal data, in which case you would class container implements ArrayAccess and use it like an array, instead of global get/set methods. Here the documentation for ArrayAccess()
$obj = new container();
$obj['key'] = "value";
echo $obj['key'];
Bottom line
Why try and reinvent the wheel? A property is a property. There is no logical or semantical improvement in wrapping every property inside another property. It's obsfucating everything. It won't be faster, it won't be clearer, it removes the oop concepts from your properties and it's just going against the current of using objects in the first place.
About easier database management
If you really want to easily pass an array to a prepared statement, you can get the properties of an object with get_object_vars($obj), no need to put them in an array before for this very purpose. Moreover, as noted by Cypher, you won't be able to use the built-in fetchObject() method, which completely nullify the time you will not have gained by having an easier time querying the database.

This will make it easy to automate DB Operations.
But will make it hard for to use the object by humans.
Yii(2) uses this setup as part of there ActiveRecords but extend it by
defining the properties as a comment
/**
* #property int $id
* #property string $name
*/
class SomeClass extends AbstractModel
And also implements magic methods: __get(), __set()` so you can easily set and get properties like this:
class AbstractModel{
public function __get($name){
if(isset($this->data[$name])){
return $this->data[$name];
}else{
throw new Exception("Undefined or property '$name'");
}
}
public function __set($name, $value){
if(isset($this->data[$name])){
return $this->data[$name] = $value;
}else{
throw new Exception("Undefined or property '$name'");
}
}

Related

PHP OOP - Pass data between classes through the calling class?

I'm struggling to find a correct approach to pass data between classes, which do not directly call each other, and are only related through a parent class (which I now use, but I consider it a dirty workaround rather than anything near a solution).
I have 3 classes both able to read input and write output, and based on configuration I set one to read, another one to write. It may even be the same class, they all share a parent class, but they are always two separate instances called from a controller class.
Currently I use this sort of functionality:
class daddy {
public static $data;
}
class son extends daddy {
public function setData() {
parent::$data = "candy";
}
}
class daughter extends daddy {
public function getData() {
echo parent::$data;
}
}
while($processALineFromConfig)
$son = new son;
$son->setData();
$daughter = new daughter;
$daughter->getData();
daddy::$data = null; //reset the data, in the actual code $daughter does that in parent::
}
Instantination of these classes runs in a loop, therefore I always need to reset the data after $daughter receives them, 'cos otherwise it would stay there for another pass through the loop.
I'm absolutely sure it's not how class inheritance is supposed to be used, however I'm struggling to find a real solution. It only makes sense the data should be stored in the controller which calls these classes, not the parent, but I already use return values in the setter and getter functions, and I am not passing a variable by reference to store it there to these functions 'cos I have optional parameters there and I'm trying to keep the code clean.
What would be the correct approach to pass data through the controller then?
Thanks!
The best option would be for two object share some other, third object. This would be the class for "third object" which will ensure the exchage:
class Messenger
{
private $data;
public function store($value)
{
$this->data = $value;
}
public function fetch()
{
return $this->data;
}
}
Then a class for both instance, that will need to share some state:
class FooBar
{
private $messenger;
private $name = 'Nobody';
public function __construct($messenger, $name)
{
$this->messenger = messenger;
$this->name = $name;
}
public function setSharedParam($value)
{
$this->messenger->store($value);
}
public function getSharedParameter()
{
return $this->name . ': ' . $this->messenger->fetch();
}
}
You utilize the classes like this:
$conduit = new Messenger;
$john = new FooBar($conduit, 'Crichton');
$dominar = new FooBar($conduit, 'Rygel');
$dominar->setSharedParameter('crackers');
echo $john->getSharedParameter();
// Crichton: crackers
Basically, they both are accessing the same object. This also can be further expanded by making both instance to observe the instance of Messenger.

Immutable objects in PHP?

Is it a good idea to create objects that cannot be changed in PHP?
For example a date object which has setter methods, but they will always return a new instance of the object (with the modified date).
Would these objects be confusing to other people that use the class, because in PHP you usually expect the object to change?
Example
$obj = new Object(2);
$x = $obj->add(5); // 7
$y = $obj->add(2); // 4
Immutable objects don't have setter methods. Period.
Everyone will expect a setXyz() method to have a void return type (or return nothing in loosely typed languages). If you do add setter methods to your immutable object it will confuse the hell out of people and lead to ugly bugs.
In my opinion objects should be immutable for value objects. Other than that it does not have much benefits unless you're sharing your object across your whole application.
There is some wrong answers here, an immutable object can have setters. Here's some implementation of immutable objects in PHP.
Example #1.
class ImmutableValueObject
{
private $val1;
private $val2;
public function __construct($val1, $val2)
{
$this->val1 = $val1;
$this->val2 = $val2;
}
public function getVal1()
{
return $this->val1;
}
public function getVal2()
{
return $this->val2;
}
}
As you can see once instantiated you cannot changed any value.
Example 2: with setters:
class ImmutableValueObject
{
private $val1;
private $val2;
public function __construct($val1, $val2)
{
$this->val1 = $val1;
$this->val2 = $val2;
}
public function getVal1()
{
return $this->val1;
}
public function withVal1($val1)
{
$copy = clone $this;
$copy->val1 = $val1;
return $copy; // here's the trick: you return a new instance!
}
public function getVal2()
{
return $this->val2;
}
public function withVal2($val2)
{
$copy = clone $this;
$copy->val2 = $val2;
return $copy;
}
}
There is several implementation possible and this is by no means an exclusive list. And remember that with Reflection there is always a way to get around that in PHP, so immutability is all in your head in the end!
It is also often good practice to put immutable objects as final.
EDIT:
changed setX for withX
added comment about final
An immutable object cannot be changed after its initial creation so having setter methods makes no sense as it goes against that base principle.
You could implement some workarounds to simulate immutability in PHP by manipulating class member visibility and overriding the magic __set() method but its not guaranteed immutable as immutability is not a feature of the PHP language.
I believe someone once wrote an extension to provide an immutable value type in PHP though so you could google for that.
Making object immutable in PHP is pretty easy. Here is an elegant and convenient approach.
All you need to do is to create the base abstract class with the specific __get() and __set() magic methods and extend this base class in the child object.
This is quite applicable if you use value objects (e.g. for DDD).
Here is the base class:
abstract class BaseValueObject
{
public function __get(string $propertyName)
{
return $this->$propertyName;
}
public function __set(string $propertyName, $value): void
{
throw new \Exception("Cannot set property {$propertyName}. The object is immutable.");
}
}
Now a child object (well, its class).
class CategoryVO extends BaseValueObject
{
public $id;
public $name;
public function __construct(array $data)
{
$this->id = $data['id'];
$this->name = $data['name'];
}
}
It would throw an exception at attempt to set some value. Basically it is immutable.
This is it.
Make as many immutable objects as you need. Create the new objects via constructor. Dispose them and re-create the new ones when needed (add a specific creator method if required, a static or an instance one, to the base class or to the extended one).
Yet such an object would conveniently expose all its properties as read-only (for some kind of serialization or the like), unlike if we would have made them private (but even though we could use JsonSerializable interface to make the serialization as flexible as we need with private properties or even more drastic transformations).
Finally one cannot mistakenly instantiate BaseValueObject as it is an abstract class. From all standpoints nice elegant solution.
I made a little trait avoiding using Reflection to ease the implementation of immutability: https://github.com/jclaveau/php-immutable-trait
Obviously, as it's not a language feature, it won't impeach mutation by magic but lighten the code of the mutators that must clone the current instance before being applied. Applied to Massimiliano's example it would produce
class ImmutableValueObject
{
use JClaveau\Traits\Immutable;
private $val1;
private $val2;
public function __construct($val1, $val2)
{
$this->val1 = $val1;
$this->val2 = $val2;
}
public function getVal1()
{
return $this->val1;
}
public function withVal1($val1)
{
// Just add these lines at the really beginning of methods supporting
// immutability ("setters" mostly)
if ($this->callOnCloneIfImmutable($result))
return $result;
// Write your method's body as if you weren't in an Immutable class
$this->val1 = $val1;
return $this;
}
public function getVal2()
{
return $this->val2;
}
public function withVal2($val2)
{
if ($this->callOnCloneIfImmutable($result))
return $result;
$this->val2 = $val2;
return $this;
}
}
You can see that you don't return $copy here but $this as Kanstantsin K noticed.
In native PHP https://secure.php.net/manual/en/class.datetimeimmutable.php has mutators that will return new instances with modification applied. So copy pasting sentences saying that immutable objects shouldn't have mutators doesn't seem super interesting.
The practice of using "withXXX" instead of "setXXX" is super interesting, thanks for the suggestion! I personnaly used "becomesXXX" for the api chainging the mutability of the instance (optionnal API in the trait SwitchableMutability).
Hoping it can help some people here!
PS: Suggestions on this little feature are really welcome :) : https://github.com/jclaveau/php-immutable-trait/issues
From an immutable object, you can get its values but there is no way to modify them. Here you can see an example of an immutable class:
<?php
declare(strict_types=1);
final class Immutable
{
/** #var string */
private $value;
public static function withValue(string $value): self
{
return new self($value);
}
public function __construct(string $value)
{
$this->value = $value;
}
public function value(): string
{
return $this->value;
}
}
// Example of usage:
$immutable = Immutable::withValue("my value");
$immutable->value();
If you want setters on a class and object this is perfectly fine, we do this all of the time as we need to set object data. Just simply don't call it immutable.
Many things in the dev world are subjective - our approaches, methodology etc - but "immutable" is a pretty solid definition:
"Immutable":
- Unchanging over time or unable to be changed.
If you want an immutable object it means it cannot be changed after instantiation. This is good for things such as data from a DB that needs to remain set in stone for the duration of the cycle.
If you need to call the object and set or change data on it after instantiation, this is not an immutable object.
Would you take 2 wheels off a car and calling it a motorbike?
There is some talk about methods on an "immutable" class being named without the word "set", but this doesn't stop the functionality of them being a method that sets data. You could call it thisDoesNotSetAnything(int $id) and allow data to be passed in which changes the object. It'll be a setter, and thus the object is mutable.

Properties in PHP

Since PHP has no notion of properties like C# what is the best way to expose certain attributes of a class? Is it recommended to have separate get and set functions or should the member variable be made public?
I am designing a class whose objects will be contained in another class. There should be a way to set/get properties of the object.
On the whole I would recommend exposing public variables on your PHP class, except where you need to perform a more complex action, in which case you would use getter and setter functions.
So where you would use this in C#
public string MyProp { get; set; }
I would recommend this in PHP
public $MyProp;
Using public attribute or private with getter/setter will both do the trick.
I will proceed like this :
If the attribute is critical, use a getter and setter like this :
class myClass
{
private $myAttr;
public function getmyAttr()
{
return $this->myAttr;
}
public function setmyAttr($attr)
{
//Do some verification on $attr
$this->myAttr = $attr;
}
}
If the attribute is less critical , you could simply set is visibility to public.
Because of the lack of strong type in php i will always prefer the private attribute with getter and setter. it let you ensure that the attribute is always what it should be (not "1" instead of 1 for example).
It depends, it is not very rare to make attributes public which are not very security sensitive. But if you do want to keep them private you could create something like this:
class withAttributes{
private $property;
public function getPrivateProperty{
return $this->property;
}
}
In the other class:
$object = new withAttributes();
$attribute = $object->getPrivateProperty();
Hope this helps
There's no idiomatic way built in the language, however you could try using the magic __get and __set methods to emulate something simillar.
Consider this pretty dumb example:
Class Foo {
private $bar;
private $baz;
public function __get($name) {
if (property_exists($this, $name)) {
if ($name == 'bar') {
return strtoupper($this->{$name});
} else {
return $this->{$name};
}
}
return null; // this is what php would do, consider throwning an exception instead
}
public function __set($name, $value) {
if (property_exists($this, $name)) {
if ($name == 'bar') {
$this->{$name} = strtolower($value);
} else {
$this->{$name} = $value;
}
}
// we just ignore this call, consider throwing exceptions, or you could make the class open to property addition in runtime too
}
}
$f = new Foo;
$f->bar = 'tickle me elmo';
print $f->bar; // prints uppercase
This way later on, if you decide to implement some read/write logic, you can hook it in the magic methods. From performance standpoint, these magic methods are slower than ordinary method calls, also arguably more surprising for the users of these classes than explicit getter/setter methods.
If you want to create something more like the built-in StdObject or basic array consider implementing the ArrayAccess interface for the [] syntax.

Dependency Injection in PHP using pimple

I'm new to DI ,using Pimple. Using: php 5.3.5 (wamp), namespaces as well.
I'm refactoring code, using it, but came to a problem (s):
I have my Container that extends from Pimple, lets call it PContainer.php:
class ReuseableContainer extends Pimple{
private function initOutterClass(){
$this['special_location_class'] = '\SpecialLocation';
$this['special_location'] = function($c){return new $c['special_location_class']($c['location_details'],$c['location']);};
}
private function initGlobalFunctions(){
$this['getGeneralDataFromArray'] = function($c){
// returning a function
return function($arr){
foreach ($arr as $key => $value){
// do something
$new_data = $c['general_data_type'];
$new_data->id = $value['id'];
$new_data->name = $value['name'];
}
}
}
public function __construct(){
$this['location_class'] = '\Location';
$this['location_details_class'] = '\LocationDetails';
$this['general_data_type_class'] = '\GeneralDataType';
// define some objects
$this['location'] = function ($c) {
return new $c['location_class']();
};
$this['location_details'] = function ($c) {
return new $c['location_details_class']();
};
$this['general_data_type'] = function ($c) {
return new $c['general_data_type_class']();
};
$this->initOutterClass();
$this->initGlobalFunctions();
}
}
global $container ;
$container = new Pimple();
// embed the SomeContainer container
$container['embed'] = $container->share(function () { return new ReuseableContainer(); });
Ok. So i got a SpecialHelper.php which holds:
final class SpecialLocation{
public $name;
public $location;
public $picture;
public function __construct($location){
$this->location; // dependent on class: Location
}
}
final class SpecialUser{
private $id;
private $location;
public function __construct(\Location $location,$id=''){
$this->id = $id;
$this->location = $location; // $container['embed']['location'];
}
and we got our GeneralHelper.php which holds:
final class Location{
public $lat;
public $lng;
public function __construct($lat='',$lng=''){ $this->lat = $lat; $this->lng = $lng;}
}
final class LocationDetails{
public $id;
public $addresss;
public function __construct($id='',$address=''){$this->id = $id; $this->address = $address;}
}
class GeneralDataType{
public $id;
public $name;
public function getName(){ return $this->name;}
public function getId(){ return $this->id;}
}
and we have our "Special Class" controller, which looks something like this:
final class SpecialController{
public function foor($some_array){
$this->doSomething($some_array);
}
private function doSomething($ret_value){
// do something
$arr = array();
foreach($ret_value as $key => $value){
$something = $container['embed']['getGeneralDataFromArray']($value);
$special_location = $container['embed']['special_location'];
$arr[] = special_location;
}
return $arr;
}
}
Finally we have our main "driver", main.php
require('PContainer.php');
....
...
$some_array = array(....);
$special_controller = new SpecialController();
$special_controller->foor($some_array);
Problems:
1) I had to add initOutterClass function inside ReuseableContainer to decouple the "Special" classes, how could have i decoupled them in a better way? creating a new "special" 9container or something? as EVERYTHING now sitts inside the container.. same goes to the initGlobalFunctions()
2) regarding SpecialHelper.php: i have there SpecialLocation, which one of its properties is a \Location class, i've put it in the constructor , but if i have 20 object properties that are dependent, i must put them all as INPUT params for the constructor?? same goes to the SpecialUser class, it has a $location which if i could i would have made $this->location = $container['embed']['location']; instead of $this->location = $location; resulting in a dependent on the DI! :/
3) I've had to create SpecialHelper.php in a different file, despite wanting to put it in the "special class controller", just so there won't be any unknowns (due to require statement order)
4) MOST importantly: about the "Special class" controller, how do i solve the doSomething method? i must create "Special Location" object inside the loop but i get that $container is unrecognized (despite being global, as of scope probably) but more over it's really dependent! and it's a private function, i don't wish to pass the container to EVERY class i'll use from now on, it isn't IoC right?
Any help is appriciated... i'm trying to understand the best practices..
Thank you
4)Most important: IoC is correct. That an implementation is not correctly working does not reflect the principle of IoC itself.
If you want to use the global $container within a function, then should you use the global keyword within that function. That is how PHP works. Making it static is solving the problem of reference, but does not make a real difference.
An IoC container resolves the dependencies for the caller. The caller does not have to know anything about the internals of the callee - and he doesn't care either. So, there should be some kind of contract by which the exchange of data is regulated. If you have that situation, then you have IoC.
3)That problem is too vague to answer, but imo also not relevant from a practical perspective. Does it work? Ok, good to know. :-)
2)The clue of IoC is the use of contracts. The IoC container is there to connect the caller to the proper contract. The contract resolves to a concrete callee. The callee will return information inline with the contract. The caller understands the answer. Therefor will you need that the input and output in this process is independent of a certain implementation at a certain time. So don't use 20 object properties as input, but use an array or general object instead.
1) I get the idea that you are mixing functional flow (data flow) with technical flow (relationships between classes). An IoC container serves the purpose of the technical flow, it optimizes the dependency in the relationships between classes. For instance, if you want to connect to a database, then might you reuse an existing connection instead of creating new connections all the time. Or if you want to use a special functionality on several moments in your flow, then might you use IoC for that.

Advice on framework design

i'm currently constructing some kind of mini-framework for a project, and come up with this solution. I have tried many of them, but this seems to me very convinient (code is shortened for simplicity):
# Basically it's just a Registry pattern
class Repository {
private static $objects = array();
public function loadObject($alias, $object) {
self :: $objects[$alias] = $object;
return true;
}
public function __get($name) {
if ($this->objectExists($name)) {
return self::$objects[$name];
} else {
return false;
}
}
}
class Database extends Repository {
/* database class */
}
class Session extends Repository {
public function some_func($key, $value) {
/* i can access database object using $this in any class that extends Repository */
$this -> database -> exec (/* sql */);
}
}
/* =================== */
# Load core objects
$R = new Repository :: getInstance();
$R -> loadObject ('config', new Config());
$R -> loadObject ('database', new Database());
$R -> loadObject ('session', new Session());
/* =================== */
Can you see any problems or drawbacks with this approach? For me i see maybe i little more memory consumption, because each next class holds more and more objects from Repository.
Before i had a design where each class was independent, but anyway all of them require database, session, config etc, no i had to declare them in any class.
Just wanted to note that i'm planning this design only for core objects, not for specific classes.
Don't extend Repository:
A database is not a repository, a repository has a database
Your database/session/config aren't related and shouldn't be. Liskov substitution principle:
[...] if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).
Edit: trying to answer follow-up questions in this reply.
This technique is called dependency injection. A session example:
class Session {
// notice the clean API since no methods are carried along from a possibly huge base class
public function __construct(ISessionStorage $storage) {
$this->_storage = $storage;
}
public function set($key, $value) {
$this->_storage->set($key, $value);
}
}
interface ISessionStorage {
public function set($key, $value);
}
class DatabaseSessionStorage implements ISessionStorage {
public function __construct(Db $db) {
$this->_db = $db
}
public function set($key, $value) {
$this->_db->query("insert....");
}
}
class CookieSessionStorage implements ISessionStorage {
public function set($key, $value) {
$_SESSION[$key] = $value;
}
}
// example where it's easy to track down which object went where (no strings used to identify objects)
$session = new Session(new DatabaseSessionStorage(new Db()));
$session->set('user', 12512);
// or, if you'd prefer the factory pattern. Note that this would require some modification to Session
$session = Session::factory('database');
$session->set('user', 12512);
Sure you could store connection settings hardcoded in a config-file. This only means the other files need to get hold of that config class without going through their parents. For example:
class Database {
// The same pattern could be used as with the sessions to provide multiple database backends (mysql, mssql etc) through this "public" Database class
public function __construct(Config $config) {
$this->_config = $config;
$this->_connect();
}
private function _connect() {
$this->_config->getDatabaseCredentials();
// do something, for example mysql_connect() and mysql_select_db()
}
}
If you'd prefer to keep config information out of php-files (for easier editing/reading), see the Zend_Config-classes for examples of accessing different storage devices including the more common ones: ini, php array, xml. (I'm only mentioning Zend_Config since I've used it and am satisfied, parse_ini_file would do as well.)
A good & hopefully easy read: Fabience Potencier - What is dependency injection?
Edit #2:
Also see the slide: Matthew Weier O'Phinney - Architecting your models
"because each next class holds more and more objects from Repository" - I don't exactly understand what you meant by that, I think as the objects are static there's only one copy.
I think you can use a little bit different approach to avoid drawback, by combining singleton pattern.
class Repository
{
private static $instance;
private $objects = array();
private static getInstance()
{
if (!Repository::$instance)
!Repository::$instance = new Repository();
return !Repository::$instance();
}
public static function loadObject($alias, $object)
{
Repository::getInstance()->objects[$alias] = $object;
return true;
}
public static function get($name)
{
$repository = Repository::getInstance();
if (isset($repository->objects[$name]
return $repository->objects[$name];
else
return false;
}
You will then use this in your child classes:
Repository::get('config');
and in bootstrap
Repository::loadObject('config', new Config());
Repository::loadObject('database', new Database());
etc.
I think it's a bad example. Keeping object such as config, database and session in internal array is unsuitable.
These objects should be explicit (probably static) properties in Your base class and should be accessed by exact name.
Purist probably would say that accessing array this is also slower than directly accessing properties :-))
I would implement these objects as singletons and maybe hide them in something like Application class.
I think overriding __get() is good for objects that have dynamic properties, or when You want to prevent accessing non-existent properties (just throw exception).
This also helps (side effect) against rising notices by PHP (I hate them :-)) when any property is undefined, but notices should be killed explicitly because they are (small but...) just BUGS!
Keep in internal array properties that belong to specific objects (with tens of properties).
I use this solution intensively in classes mapped to db table records.
Regards.

Categories