Enforceable array structure in PHP using ArrayObject? - php

I've a set of data (~30 properties, all having their own array of values) that I want to pass around to various classes in PHP and I want to also enforce the data's array structure. Multiple classes will be expecting this structure to be consistent.
Because of these facts I can't really rely on a standard array and so I decided to pass an object around. I looked into ArrayObject and while it allows me to set/get as if the class were an array I didn't see anything about enforcing the structure.
Is there an existing standard class that can handle enforcement of it's array-like structure while still being treated as an array, e.g., basically ArrayObject + enforcement?
An example of the array structure:
$item_type_props = array(
'phone' => array('speed' => 1000, 'self_label' => false, 'support_poe' => true, 'bluetooth' => false),
'pbx' => array('acd_support' => true, 'max_conn' => 300, max_phones => 600),
'adapter' => array('fxo' => 4, 'fxs' => 0, 't1_e1_pri' => 0),
etc...
);
I know each property in the array could be it's own class and enforce it's own fields through the constructor and set/get but then I suddenly have ~30 classes that are nothing but a bunch of attributes and that seems somewhat excessive for just holding data.
Alternatively, if I'm just approaching this from the wrong mindset and missing something really, really obvious then please do point it out. I get the feeling that I am but my brain might be having a vacation.

While you could roll your own, I encourage you to use an existing validation implementation. For example, Symfony\Validator allows you to define nested structures and the requirements on each level:
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;
$validator = Validation::createValidator();
$constraint = new Assert\Collection(array(
// the keys correspond to the keys in the input array
'name' => new Assert\Collection(array(
'first_name' => new Assert\Length(array('min' => 101)),
'last_name' => new Assert\Length(array('min' => 1)),
)),
'email' => new Assert\Email(),
'simple' => new Assert\Length(array('min' => 102)),
'gender' => new Assert\Choice(array(3, 4)),
'file' => new Assert\File(),
'password' => new Assert\Length(array('min' => 60)),
));
$violations = $validator->validate($input, $constraint);
This lets you push the details of how to validate down to another (already tested) level, while letting your code focus on why it needs this data. For the Symfony case, you can use an array as the storage mechanism, and use a design that firewalls unvalidated from validated data.
One way we might do this is notationally. Pretend we have implemented a method, perhaps using Symfony's validator, to return a validated array. We can use Hungarian notation to indicate our structure has passed through validation and is "safe":
<?php
$vInput = validate($_GET); // Hungarian notation: any variable beginning with "v" is "validated" and safe to use
function foobar(array $vInput) { ... }
While this is performant, it's not great for maintenance in the long term. So, you might consider an object wrapper that allows you to leverage the type system:
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;
class ValidatedArray extends \ArrayObject {
public function construct($input = [], $flags = 0, $iterator_class = 'ArrayIterator') {
$violations = Validation::createValidator()->validate($array, $this->constraints());
// throw exception if any violations
parent::__construct($input, $flags, $iterator_class);
}
public function __offsetSet($index, $value) {
$constraints = $this->constraints()[$index]; // specific constraints to this index
$violations = Validation::createValidator()->validate($array, $constraints);
// throw exception on violations
parent::__offsetSet($index, $value);
}
public function constraints() {
return new Assert\Collection(...);
}
}
$input = new ValidatedArray($_REQUEST); // first time validation
$input['foo'] = 'bar'; // also subject to validation
You might want to make this implementation an abstract base class, with concrete descendants implementing the constraints method to provide the specific limitations on the array object itself. This provides a flexible way to get dedicated Data Transfer Objects.

Generally speaking I would argue that - unless you are passing data to another context, e.g. javascript - a PHP application should be nicely organized in PHP classes. This is simply the easiest way to enforce the structure. Your are right, this might result in quite straightforward DTO's with a bunch of getters and setters, but it will beat checking array structures for sure. In your case there also appears to be a relationship in the array, otherwise it would not make sense combining them into an array at all.
Using PHP7 you can clearly define the method signature and enforce the types, e.g.
public function setSomething(string $myValue)
{
$this->something = $myValue;
}
Same with return types:
public function myActionMethod(array $options): ActionRespsonse
{
// Do something
}
If you have more complex data types, I would recommend using Value Objects. These are nothing more but simple PHP classes that represent a more complex value. For example, a phone number:
public function setPhoneNumber(PhoneNumber $phoneNumber)
{
$this->phoneNumber = $phoneNumber;
}
Here the PhoneNumber is a Value Object which effectively is a tiny class by itself which enforces its usage:
class PhoneNumber {
private $phoneNumber;
public __construct(string $phoneNumber) {
if (strlen($phoneNumber) != 10) {
throw new \Exception('Not a valid phone number');
}
$this->phoneNumber = $phoneNumber;
}
}
This is also where the validation can tie into the answer from #bishop as you can use an existing Validator to help you out. You can find an example of a Value Object of a Phone Number here (just googled it): Example Phone Number Value Object
I do have the feeling that you might be converting your PHP data to an array for another reason? Like interacting with a database, or passing it on to a different context, such as Javascript?
In that case, once you have your DTO's and VO's neatly defined you can then consider serializing them e.g. to/from JSON. You can use Symfony libraries for that, as described here: Symfony Serializer
If you really want arrays you can also consider hydrating them to/from entities, using the library from Marco Pivetta (ocramius) who is pretty much the authority on hydration an approach extensively used in Doctrine: Ocramius Hydrator
Plenty of options, eh!?
To be honest, though, IMHO typically there would need to be a pretty good argument to pass these complex arrays around as arrays offer very little in supporting functionality. In addition, your code will very likely and very quickly become difficult to read and maintain, as at every point where there would be some kind of modification of the array, or usage of its data elements, you would need to implement all kinds of checks or run a validation as described by #bishop. I would not allow something like that to exist in our applications code bases...
So, concluding: If you have a tightly defined set of PHP Objects with well established constructor, properties, getters, setters, constructor, relationships, action methods and besides that some kind of serializing mechanism, you're in good shape. Also other developers will be 'forced' to work with the objects and provided interface (methods and VO's) thus guaranteeing a degree of maintainability and quality.
By the way, you might consider reasing Martin Fowler's thoughts about these things: Martin Fowler: DTO Martin Fowler: VO Martin Fowler: DTO2

Related

How to use a Yii fixture object that's not in the defaultScope? Is there a way of using resetScope() with Yii fixtures?

Here's a test file:
class MyTest extends CDbTestCase
{
public $fixtures = array(
'my_data' => 'MyData',
);
public function testMyFunction()
{
$myObjectNotInDefaultScope = $this->my_data('out_of_scope_object');
//Can't do anything with $myObjectNotInDefaultScope since it returns null
// Is it possible to use resetScope?
// I can always set a primary key for the object and use findByPk but that's a hack
}
}
and here's the corresponding fixture:
<?php
return array(
'out_of_scope_object' => array(
'title' => 'This one is out of scope',
'status' => 'archived', // so not in the default scope
),
'in_scope_object' => array(
'title' => 'This one is in scope',
'status' => 'active',
),
);
Both rows in the fixture are added to the db table, so that's not the problem. I can access both rows via the primary keys that they're allocated. But I can't access the out of scope object in this way:
$myObjectNotInDefaultScope = $this->my_data('out_of_scope_object');
which when you're testing is really how you want to access it, I think.
I have a less than satisfactory solution in use for now of allocating the object a primary key value and using findByPk (edit: with resetScope()) to load the object. I would prefer to use the normal way of working with fixtures instead, if that's possible.
Edit: To clarify a little in response to some posts:
It is possible to use fixtures as a method to return an object. This would work:
$myObjectInDefaultScope = $this->my_data('in_scope_object');
but this wouldn't work BECAUSE it's not in the default scope and there's seemingly no way currently of running resetScope() for a fixture method call:
$myObjectNotInDefaultScope = $this->my_data('out_of_scope_object');
Why do I need to do this? Well, I might want to test my unarchive method, for example. Seems reasonable to me. (As mentioned before, I can get round this a little inelegantly by using a primary key to load the object corresponding to the fixture).
Although I can access the fixture data as an array using:
$arrayNotInDefaultScope = $this->my_data['out_of_scope_object'];
it's returning an array not an object, so I can't test the object's methods on an array.
To answer my own question, there is currently no way to use resetScope() with Yii fixtures (v 1.14). It could be implemented with some effort but given that Yii2 is on its way, it's probably not worth the effort of generating a pull request that may never make it in to the source.
For now, IMO, the cleanest workaround is:
1) Define a primary key in the fixture
2) Get the primary key from the fixture array and look up the object using it:
$arrayOutOfScopeObject = $this->my_data['out_of_scope_object'];
$myObjectNotInDefaultScope = MyObject::model()
->resetScope()
->findByPk($arrayOutOfScopeObject['id']);
You could, of course, save yourself the effort of looking up the pk from the fixture by hard-coding the pk value in your test code, but that leaves your test code vulnerable to being broken by changes to a fixture that's shared with other tests.
You are using the fixtures as a method, whilst it is an array of object.
So instead of:
$myObjectNotInDefaultScope = $this->my_data('out_of_scope_object');
You should be doing:
$myObjectNotInDefaultScope = $this->my_data['out_of_scope_object'];
Check the guide for more info

Doctrine_Query, criteria as objects, & the Visitor Pattern. Possible?

I've been working on a bunch of projects lately with Doctrine 1.2 integrated into Zend Framework 1.11, and it has been really fun.
One of the most common methods I have implemented in my service layers are methods which returns a collection of my domain models according to a set of criteria being passed as arguments.
I've been creating them with interfaces like this:
//All books
$books = $service->getBooks();
//Books under certain categories and authored which matches the search term 'Hitchens'
$books = $service->getBooks(array(
'category' => array(1,2,3,4),
'author' => array('like' => '%Hitchens%', 'diedBefore' => Zend_Date::now()),
'orderBy' => 'bookTitle',
'limit' => 10
));
And the implementation look like this:
public function getBooks(array $options = null)
{
$query = Doctrine_Query::create()->from('LibSys_Model_Book as book');
if($options !== null){
if(isset($options['author']){
//Manipulate Doctrine_Query object here....
}
//More 'ifs' and manipulations to the Doctrine_Query object...(additional criterias, orderBy clauses, limits, offsets, etc.)
}
}
And as my the need for more criteria rises, the messier the implementations get. And needless to say, there are a lot of code re-use everywhere, and updating the code is so such a tedious task.
I've been thinking that it would be a lot better if I can pass in the criteria as objects instead of array segments, utilizing the Visitor Pattern with something like this:
$authorCriteria = new LibSys_Criteria_Author();
$authorCriteria->like('%Hitchens%');
$authorCriteria->diedBefore(Zend_Date::now());
$books = $service->getBooks(array(
$authorCriteria,
new LibSys_Criteria_Category(array(1,2,3,4))
));
With LibSys_Criteria_Category and LibSys_Criteria_Author implementing a common interface which might look like this:
interface LibSys_Doctrine_Criteria_Interface
{
public function applyCriteria(Doctrine_Query $query);
}
Therefore, it will just be a matter of looping through the set of criteria objects provided and pass along the Doctrine_Query object to them as they take turns in manipulating it to suit the needs:
public function getBooks(array $criteria = null)
{
$query = Doctrine_Query::create()->from('LibSys_Model_Book as book');
if($criteria !== null){
foreach($criteria as $criterion){
if($criterion instanceof LibSys_Doctrine_Criteria_Interface){
$criterion->applyCriteria($query);
}
}
}
}
This way not only does it make them easier to use, but also make the services extendible, the criteria objects reusable, and everything much easier to maintain.
In order to make this work robustly, though, one would need to be able to inspect the Doctrine_Query object and check any existing joins, conditions, etc. This is because it is not impossible that two totally different criteria may require same set of conditions or same set of joins. In which case, the second criteria can either simply re-use an existing join and/or adjust accordingly. Also, it wouldn't be hard to imagine cases where a criterion's set of conditions are mutually incompatible with another, in which case should should warrant an exception to thrown.
So my question is:
Is there a way to inspect a Doctrine_Query and get information regarding which components has been joined, or any set of conditions already applied to it?

PHP/JS Form Class - Array vs OO

I have recently begun working on a PHP/JS Form Class that will also include a SQL Form builder (eg. building simple forms from sql and auto inserts/updates).
I have tried several classes (zend_form, clonefish, PHP Form Builder Class, phorms etc) but as yet haven't come across a complete solution that is simple, customizable and complete (both server side and client side validation, covers all simple html elements and lots of dhtml elements: sorting, wysiwyg, mutli file upload, date picker, ajax validation etc)
My question is why do some "classes" implement elements via an array and others via proper OO class calls.
eg.
Clonefish (popular commercial php class):
$config = Array(
'username' => Array(
'type' => 'inputText',
'displayname' => 'Username',
validation => Array(
Array(
'type' => 'string',
'minimum' => 5,
'maximum' => 15,
),
),
));
$clonefish = new clonefish( 'loginform', 'test.php', 'POST' );
$clonefish->addElements( $config, $_POST );
Then others eg. Zend_Form
$form = new Zend_Form;
$username = new Zend_Form_Element_Text('username');
$username->addValidator(new Zend_Validate_Alnum());
$form->addElement($username);
I realise Zend_Form can pass elements in via an array similar to clonefish but why do this?
Is there any benefit? It seems to make things more complicated especially when using a proper IDE like Komodo.
Any thoughts would be appreciated as I dont want to get too far down the track and realize there was great benefit in using arrays to add elements (although this wouldn't be much of a task to add on).
Cheers
My question is why do some "classes" implement elements via an array and others via proper OO class calls.
For convenience. It's less verbose and it feels less like coding and more like configuration and you need less intimate knowledge of the API.
Btw, the reason you have not yet come across a complete solution that is simple, customizable and complete is because it is not simple. Forms, their validation and rendering is complex, especially if you want to have it customizable for any purpose. ZF's form components are a good example of how to properly decouple and separate all concerns to get the ultimate extensible form builder (including client side code through Zend_Dojo or ZendX_Jquery). But they are also a great example of the complexity required for this. Even with the convenient array configuration, it is damn difficult to make them bend to your will, especially if you need to depart from the default configuration and rendering.
Why to use objects? Becouase they are a much more complex types. Consider the following example (I never useed Zend_Form so I don't even know its architecture):
class MySuperAlnumValidator extends Zend_Validate_Alnum {
protected $forbiddenWords = array();
public function addForbiddenWord($word) {
$this->forbiddenWords[] = $word;
}
// Override Zend_Value_Alnum::validate() - I don't know whether such a method even exists
// but you know what's the point
public function validate() {
parent::validate();
if (in_array($this->value, $this->forbiddenWords) {
throw new Exception('Invalid value.');
}
return $this->value;
}
}
// -----------------------
$validator = new MySuperAlnumValidator();
$validator->addForbiddenWord('admin');
$validator->addForbiddenWord('administrator');
$username->addValidator($validator);
This is only a simple example but when you start writing more complex validators/form fields/etc. then objects are, in principle, the only meaningful tool.

Does this function have too many parameters?

In the end, I got this function. I don't know whether it's normal or not.
function user_registration($user_name, $user_email, $user_pass, $address,
$city, $postalcode, $country, $phone, $mobilephone)
How and why can I improve this?
You could either pass an array with all variables packed nicely together, or just make a "User" class and add all properties via setters and do the validation in the end with a dedicated method:
class User {
public function setName($name) {
$this->name = $name;
}
[...]
public function register() {
//Validate input
if (empty($this->name))
$this->errors[] = "ERROR, Username must not be emtpy";
//Add the user to the database
//Your SQL query
return empty($this->errors);
}
}
$user = new User();
$user->setName("Peter");
$success = $user->register();
if (!$success)
echo "ERRORS OCCURED: ".print_r($user->errors, true);
A solution would be to only have one parameter, that can contain several pieces of data -- like an array.
Your function could be defined this way :
function user_registration(array $data) {
// work with $data['name']
// and $data['email']
// ...
}
And you'd call it like this :
user_registration(array(
'name' => 'blah',
'email' => 'test#example.com',
'pass' => '123456',
// and so on
));
Nice things are :
You can add / remove "parameters" easily
The "parameters" can be passed in any order you want
Not so bad things are :
No hint while typing in your IDE
No documentation (like phpDoc)
I personally don't think it has too many parameters. From looking at the functions definition it is clear what you require as input which wouldn't be so obvious if called with an array.
"If it aint broke don't fix it!"
When you look at your argument names, you cannot but notice that they can be grouped into three different groups:
User Data: $user_name, $user_pass
Address Data: $address, $city, $postalcode, $country
Contact Data: $user_email, $phone, $mobilephone
Consequently, you could apply Introduce Parameter Object:
Often you see a particular group of parameters that tend to be passed together. Several methods may use this group, either on one class or in several classes. Such a group of classes is a data clump and can be replaced with an object that carries all of this data. It is worthwhile to turn these parameters into objects just to group the data together. This refactoring is useful because it reduces the size of the parameter lists, and long parameter lists are hard to understand. The defined accessors on the new object also make the code more consistent, which again makes it easier to understand and modify.
If you dont want to do OOP you could group the arguments into arrays as well but you'll lose all the type benefits then. I will just assume you don't mind using objects. So, after applying the Refactoring you'll end up with
function user_registration(User $user, Address $address, Contact $contact)
Looking at that parameter list should make you notice that Address and Contact likely belong to User in the first place, so you could consider changing the function signature to just
function user_registration(User $user)
and then call it like this:
$user = new User('johndoe', 'secretsauce');
$user->setAddress(new Address('Doe Street', 'Doe Town', 12345, 'Neverland'));
$user->setContact('jdoe#example.com', '+123 12345', '+123 54321');
user_registration($user);
We could probably make username and password into a Credentials object as well and then just do
user_registration(new User($credentials, $address, $contact));
By requiring the data in the ctor we make sure newly registered users do have all these information. We could argue whether we need Address and Contact for registering users though, so Setter injection might be good enough here:
$user = new User(new Credentials('johndoe', 'secretsauce'));
$user->setAddress(new Address('Doe Street', 'Doe Town', 12345, 'Neverland'));
$user->setContact(new Contact('jdoe#example.com', '+123 12345', '+123 54321'));
user_registration($user);
However, user_registration as a separate function in global scope is misplaced. By GRASP's Information Expert principle, methods should be on the objects that have the most information to fulfill the responsibility. This improves Cohesion and reduces Coupling. In other words:
$user = new User($credentials);
$user->setAddress($address);
$user->setContact($contact);
$user->register();
One problem with the User class now is that it contains the password. The password is only ever needed to authenticate the user against the authentication service. We could argue about the username, but the password definitely should not be part of the User object at all. So you should do something like
$user = new User;
$user->setAddress($address);
$user->setContact($contact);
$user->register($credentials);
and when register() is called, it will only use the credentials to delegate insertion of a new user into the user storage. But it will not retain them in the actual User instance.
Finally, you might want to add a Simple Factory or Builder pattern to encapsulate the creation of the User to simplify the aggregation of the various instances. Or you might want to introduce a Repository pattern and move the register() method there. This is beyond scope for this question though.
As a general rule of thumb (not as a steadfast rule), anytime you have to ask "Does this function have too many parameters?" -- the answer is yes. Your intuition is telling you something that your brain just hasn't yet been able to work out.
In this particular case, the first thing that comes to mind is that your user cred.s should be checked first (does the username already exist? is the pw complex enough) and your user details should be added separately, probably using an object or array.
One way is to pass an array as parameter to this function and put all info in that array:
function user_registration(array $user_info)
{
// process $user_info;
}
Function (method) without any parameters is best. Function with one paremeter is better than function with 2 parameters. Function with 2 parameters is better than function with 3 parameters and so on.
#Florianh has given a perfect solution how your code can be improved. With this comment, I would like to elaborate on the "why" part from a system design perspective.
From an Object Oriented perspective, other objects should be able to manipulate attributes. That is why attributes should never be defined as "public". They should be "private" e.g.:
private var $name;
The reason for this is when other objects would manipulate this variable, the correct working of the object is at risk. Methods, on the other hand, can be defined publicly:
public function register()
Accordingly, the manipulation of the attributes will happen through the appropriate methods. A method can be used to also evaluate the correctness of operations on the attributes.
There are two operations that can happen: reading the current value of an attribute by using Get methods and saving a new value of an attribute by using Set methods.
A good practice would be to implement a get method for each class attribute. Each attribute that can be modified, should have an appropriate set method as well.
Sometimes it is better to not implement a get/set method (e.g.: showData()) after all. This is because the usage of getters and setters within a particular class may possibly lead to a reduced performance. However, this means that when changing or implementing the class, one must be careful when trying to save false information and as a result putting the integrity of the class at risk.
Now, consider the fact that you decide to use only one phone number instead of both a phone- and mobile number. When the mobile number becomes deprecated, your main program still remains the same. All you have to do is change/remove one method. The advantage of using getters and setters is an increase in adaptability and maintainability.
I make array of keys like
$fields = array('field1', 'field2');
function register (array $values, array $keys)
{
$data = array();
foreach ($keys as $one)
{
if (isset($values[$one])) $data[$one] = $values[$one];
}
// or you can use array functions like array_flip and after - array intersect
}
I'd make it this way
fields=explode(",","name,surname,lastname,street,city,region,zip,country");
user_registration($fields);
Because I am sure these variables coming from $_POST

What is the best way to represent configuration options internally?

So, I am looking at a number of ways to store my configuration data. I believe I've narrowed it down to 3 ways:
Just a simple variable
$config = array(
"database" => array(
"host" => "localhost",
"user" => "root",
"pass" => "",
"database" => "test"
)
);
echo $config['database']['host'];
I think that this is just too mutable, where as the configuration options shouldn't be able to be changed.
A Modified Standard Class
class stdDataClass {
// Holds the Data in a Private Array, so it cannot be changed afterwards.
private $data = array();
public function __construct($data)
{
// ......
$this->data = $data;
// .....
}
// Returns the Requested Key
public function __get($key)
{
return $this->data[$key];
}
// Throws an Error as you cannot change the data.
public function __set($key, $value)
{
throw new Exception("Tried to Set Static Variable");
}
}
$config = new stdStaticClass($config_options);
echo $config->database['host'];
Basically, all it does is encapsulates the above array into an object, and makes sure that the object can not be changed.
Or a Static Class
class AppConfig{
public static function getDatabaseInfo()
{
return array(
"host" => "localhost",
"user" => "root",
"pass" => "",
"database" => "test"
);
}
// .. etc ...
}
$config = AppConfig::getDatabaseInfo();
echo $config['host'];
This provides the ultimate immutability, but it also means that I would have to go in and manually edit the class whenever I wanted to change the data.
Which of the above do you think would be best to store configuration options in? Or is there a better way?
Of those 3 options, a static method is probably the best.
Really, though, "the best" is ultimately about what's easiest and most consistent for you to use. If the rest of your app isn't using any OO code then you might as well go with option #1. If you are ultimately wanting to write a whole db abstraction layer, option #2.
Without knowing something more about what your goals are and what the rest of your app looks like, it's kind of like asking someone what the best motor vehicle is -- it's a different answer depending on whether you're looking for a sports car, a cargo truck, or a motorcycle.
I'd go with whats behind door #3.
It looks easier to read and understand than #2, and seems to meet your needs better than #1.
Take a look at this question for ideas on storing the config data in a separate file:
Fastest way to store easily editable config data in PHP?
I'd use method #2 pulling the config data as an array from an external file.
The best way is that which fits your application best.
For a small app, it might be totally sufficient to use an array, even it is mutable. If no one is there to modify it except you, it doesn't have to be immutable.
The second approach is very flexible. It encapsulates data, but does not know anything about it. You can pass it around freely and consuming classes can take from it what they need. It is generic enough to be reused and it does not couple the config class to the concrete application. You could also use an interface with this or similar classes to allow for type hints in your method signatures to indicate a Config is required. Just don't name it stdDataClass, but name it by it's role: Config.
Your third solution is very concrete. It hardcodes a lot of assumptions about what your application requires into the class and it also makes it the responsibility of the class to know and provide this data through getters and setters. Depending on the amount of components requiring configuration, you might end up with a lot of specific getters. Chances are pretty good you will have to rewrite the entire thing for your next app, just because your next app has different components.
I'd go with the second approach. Also, have a look at Zend_Config, as it meets all your requirements already and let's you init the Config object from XML, Ini and plain arrays.

Categories