Even though there's some discussions regarding this issue I wanted to check on certain example what would be the best approach.
Instead of using existing solutions I created my own persistence layer (like many do)
So my approach is also in question here.
For every table in db I have model class that has appropriate getters and setters and some mandatory methods. I also created only one generic DAO class that handles all types of model objects.
So, for example to save any model object I instantiate genericDAO class and call save method that I pass model object as attribute.
Problem is that in runtime genericDAO class doesn't know whitch model object it gets and what methods (getters and setters) exist in it, so I need to call mandatory model class method that retrieves list of attributes as multiple string array.
For example for every attribute there's array(table_column_name,attribute_name,is_string).
When I call save function it looks like this:
public function save(&$VO) {
$paramArray = $VO->getParamArray();//get array of attributes
$paramIdArray = $paramArray[0]; //first attribute is always id
/*create and execute getId() and store value into $void to check if it's save or update*/
eval('$voId = $VO->get'.ucfirst($paramIdArray[1]).'();');
...
Currently I'm using eval to execute those methods, but as it is well known eval is very slow.
I'm thinking of changing that into call_user_func method
Something like:
$voId = call_user_func(array($VO, 'get'.ucfirst($paramIdArray[1])));
But also there's other solutions. I can maybe use something like this $method = 'get'.ucfirst($paramIdArray[1]));
$voId = $VO->$method();
or else
$method = 'get'.ucfirst($paramIdArray[1]));
$voId = $VO->{$method}();
What would be the best way?
First of all, there's no need to pass references like you are doing. You should give this a read to try to understand how PHP handles object references.
So public function save(&$VO) { should become public function save($VO) {.
Second, there is no need to use eval (in fact, it's better not to because of speed, debugability, etc). You can't stack-trace an eval call like you can a dynamic one.
Third, call_user_func is all but useless since PHP supports dynamic variable functions. Instead of call_user_func(array($obj, $method), $arg1), just call $obj->$foo($arg1). The call_user_func_array function is still useful since it supports variable length arguments and supports passing references.
So, ultimately, I would suggest this:
$method = 'get' . ucfirst($paramIdArray[1]);
$voId = $VO->$method();
Note that there's no need to call method_exists, since it may be callable and not exist due to __get magic method support...
I normally would use:
$method = 'get'.ucfirst($attribute);
if(method_exists($obj, $method){
$obj->$method();
}
But unless there is a very good reason i would just return a key => value array from getParamArray. And operate on that instead of using the getters...
Related
In PHP it is possible to get a full class name via class name resolution like this:
Example:
namespace Name\Space;
class ClassName {}
echo ClassName::class;
Output: Name\Space\ClassName
This is better than using the string Name\Space\ClassName directly in the code because code introspection especially in IDEs can find an error directly.
I wonder if there is something similar for methods of a class - this would be specifically useful for callback functions.
This is how you can basically can pass a callback:
$a = function($callback,$arg) { return $callback($arg); }
$a('getInfo',5);
Instead of passing a string here (which might change), I would prefer to do something like this:
$a(MyClass::class::getInfo,5);
With I "go to declaration" click in the IDE I could go directly to getInfo plus I see errors in case with method does not exist anymore. Is there a way to achieve what I want to do here?
In fact, you work with callable type. And PHP allows setting method/function name only as a string. But if you use classes and objects you will have a different way to set callback. For example:
$a = function($callback, $arg) {
return call_user_func($callback, $arg));
}
// call a static method of the class with
// using fullname space and method name as a string
$a('Name\Space\MyClass::getInfo',5);
// call a static method of the class
// with using ::class
$a([MyClass::class, 'getInfo'], 5);
// call a method of an object
$myObject = new MyClass();
$a([$myOject, 'getInfo'], 5);
Three possibilities.
(1)
echo `__CLASS__`;
...returns namespace\classname as a string.
(2)
If you're trying to get the namespace\classname from another class, i.e., not the one where you're currently executing code, then I would suggest setting a public property inside each class such as:
public static $classname = __CLASS__;
which you could then access from anywhere as:
ClassName::$classname
Put it in each of your classes. Always use the same property name.
(3)
Have you considered the PHP function debug_backtrace() which returns a call stack with the most recent call at index = 0;
So, if:
$caller = debug_backtrace();
Then, $caller[0]['class'] contains the fully qualified class name, including any namespace, at the point where you called debug_backtrace().
I'm guessing that #2 is the solution that will work for you.
Just thought of a 4th possibility that doesn't depend on you adding any code to each class. Might add some overhead though, but so does my 3rd solution above.
(4)
$declared_classes = get_declared_classes();
This lists all of the classes currently declared within the PHP scope as fully qualified namespace\classname. You could search the returned array for partial string matches within the array and return the whole namespace\classname string.
One thing to keep in mind. You might have duplicates if different namespaces have same-named classes.
I've added this as a comment somewhere else but figured it might warrant an actual answer to this question. If you use:
$callback = [MyClass::class, 'myMethod'];
Then at least one IDE (PhpStorm) will recognize this as the callable that it is, allow you to navigate to it, mention it in "show usages" and automatically change it when it is renamed through a refactor. I use this in my code if, for instance, I reference a method in a test:
$this->mock(MyClass::class, function(MockInterface $mock) {
$mock->shouldReceive([MyClass:class, 'myMethod'][1])->andReturn(10);
});
Not the cleanest syntax, but it's workable.
I have done a few projects lately using a Database Object super class which I use for quick one off record query/update, and extending with appropriate classes, such as a User class.
I found that many classes I was writing had the exact same methods: query_values(), update(), delete(), etc.
So I came up with a class with a constructor that looks like this:
public function __construct($table, $db_object, $record_id = null){
$this->db = $db_object; // Database object with query methods
$this->table = $table; // The name of the database table
$this->get_column_data();
if(!is_null($record_id)){
// This retrieves all column values,
// stores into private $fields array property
$this->query_values($record_id);
}
}
And a child class constructor looks like this:
public function __construct($db_object, $record_id = null){
parent::__construct($this->table, $db_object, $record_id);
}
Where the $table property is defined at the top, as we should know which table this specific object works with.
Now, all common record management methods are in one place, and methods specific to the class are all that is defined in their respective child-classes.
The biggest drawback I see here is that all data fields are pulled and are encapsulated within the $fields property, so either generic get and set methods need to be defined (which I typically do) which almost negates the encapsulation*, or a method must be defined specifically for each property we want to expose.
*Example:
$user_id = $User->id; // NOT USING MY METHOD
vs.
$user_id = $User->_get('id'); // ACCESSES $User->fields['id']
Do you see this as a drawback, or a plus? The goal being ease of use, object-orientation (encapsulation), and just being plain awesome!
Well, you could make your life easy and use PHP's magic overloading __call method to create generic getters and setters. You could add the following method to your "Database Object super-class":
/**
* Create magic getter and setter methods to access private $fields array
*/
public function __call($method, $args)
{
$prefix = substr($method, 0, 3);
$prop = lcfirst(substr($method, 3));
if (isset($this->fields[$prop])) {
if ($prefix == 'get') {
return $this->fields[$prop];
} elseif ($prefix == 'set') {
if ( ! isset($args[0])) {
$msg = 'Missing argument: ' . get_class($this) . "::$method must specify a value";
throw new InvalidArgumentException($msg);
}
$this->fields[$prop] = $args[0];
return;
}
}
$msg = 'Invalid method: ' . get_class($this) . "::$method does not exist";
throw new BadMethodCallException($msg);
}
So let me explain what's going on here. The magic __call method will receive calls to any object method that does not match one of the object's concrete methods. It receives as parameters the name of the method that was called and an array of its arguments.
The __call method above does a quick substr check to see if the method was a "getter" or "setter" (using the first three letters of the method name). It expects that your $fields array stores lower-case "property" names (lcfirst) and uses everything after the setter/getter prefix as the expected property name.
If a property matches the getter method then that property it is returned. If not, the SplException BadMethodCallException is thrown. This is a best practice since the inclusion of the Spl exceptions in PHP.
Likewise a setter method will also throw the SplException InvalidArgumentException if no argument was specified.
PHP's magic methods will change your life. You can also use __get and __set to assign $fields array values in a similar fashion without making faux method calls. Get excited :)
You can have the best of both worlds by implementing a few magic methods on your objects. For example, by implementing __get, __set and __isset you can make the property accesses look "natural" (e.g. $user->id) while at the same time not having to define properties in each separate subclass.
For example, the implementation of __get could check at runtime the $fields member to see if the property you are attempting to get is valid for the specific object class. You can write this generic implementation once in the base class, and just populate $fields accordingly to make it work with any kind of object. This is actually what all modern mapping tools do, including probably all of the PHP frameworks that you have heard of.
As an aside, I hope you are caching the table schema per-class inside get_column_data. It would be really inefficient to requery the database for the schema of the same table every time you construct an object of the corresponding class.
There are two major approaches:
PHP file creation
All big ORM / database projects I know do not let the user write all getter methods himself. Both Doctrine and Propel use automatical generators to create real PHP files with the getter methods when you call a command line script. These files contain so called Base methods which are automatically extended by some classes you will put your own code in (so recreation of the base class will not delete your own code).
Advantage of file creation is that you have a well defined interface (some people dislike it when callable methods are not visible directly within the source code).
Magic methods / overloading
A database engine I have seen at client’s application used magic methods to simulate getters and setters. You could either use magic methods __get() and __set() or __call() (called “overloading” in PHP documentation) for this. Which method to use depends on how you want to access your values (most common $obj->property or $obj->getProperty()).
Advantage of overloading is that you do not need to write a complex PHP code generator, nor do you need to call a command line script each time you change your database design.
I am having some headaches regarding method chaining for a quite simple PHP class that returns a value, which sometimes need to go through a decryption process:
$dataset = new Datacontainer;
$key = $dataset->get('key');
$key2 = $dataset->get('key')->decrypt();
The get method is where the return lives. So the call to the decrypt method on the second row isn't going to work in its current state.
Can I do something to setup the get method to return only when nothing is chained to it, or what would be the best way to re-factor this code?
The get() method doesn't actually know whether anything is chained to it or not; but if the get() method returns nothing (null) PHP will complain about the attempt to call the decrypt() method on a non-object.
What you could do is pass an additional argument into the get() method that indicates whether it should return a value, or the object with the decrypt method.
$key = $dataset->get('key');
$key2 = $dataset->get('key',true)->decrypt();
Can I do something to setup the get
method to return only when nothing is
chained to it ... ?
No. That's not possible. (Maybe using highly complicated reflection-technology, but that's not practical, if possible at all.)
or what would be the best way to
re-factor this code?
I think there is something wrong with the structure of your class. Usually a method that does something but returns the working-instance, changes the state of the class/instance, e.g. through an attribute, that again can be fetched through a special/getter method.
$dataset->get('key') has to return an object in which decrypt() is a method. It isn't clear what class your decrypt() method is a part of. If it's a part of your $dataset class then you you need to call it in two lines:
$key2_encrypted = $dataset->get('key');
$key2 = $dataset->decrypt($key2_encrypted);
Based on my current understanding of the process I would take the following approach:
An object that decrypts data is providing a service. Such objects are most often passed in
via the constructor as a collaborator:
class ClientClass {
private $decryptor
public function __construct(Decryptor $decryptor) {
$this->decryptor = $decryptor;
}
public function doSomethingWith(DataSet $data) {
$key = $DataSet->getKey();
// do stuff, decide whether there are encryption needs
$encryptedKey = $this->decryptor->decrypt($key);
}
}
Note that there is room for improvement here with the getting of the key from the dataset (then I would need to know more about what is being accomplished, hence the name ClientClass).
I'm writing a little homebrew ORM (academic interest). I'm trying to adhere to the TDD concept as a training exercise, and as part of that exercise I'm writing documentation for the API as I develop the class.
Case in point - I'm working on a classic "getCollection" type mapper class. I want it to be able to retrieve collections of asset X (let's say blog posts) for a specific user, and also collections based on an arbitrary array of numeric values. So - you might have a method like any one of these
$User = $UserMapper->load(1);
$ArticleCollection = $ArticleMapper->getCollection(range(10,20));
$ArticleCollection = $ArticleMapper->getCollection($User);
$ArticleCollection = $ArticleMapper->getCollection($User->getId());
So, in writing the documentation for the getCollection method - I want to declare the #param variable in the Docblock. Is it better to have a unique method for each argument type, or is it acceptable to have a method that delegates to the correct internal method/class based on argument type?
It is acceptable to have a method that delegates to the correct internal method. You could document it like this:
#param Array|User|Integer $argName optional explanation
but then again, there is no one hindering you having one method each
public function getCollectionByRange(array $range)
public function getCollectionByUser(User $user)
public function getCollectionByUserId($id)
In addition, you could use the magic __call method to pretend the above methods exist and then capture method calls to them and delegate to your internal methods (ZF does that f.i. for finding dependant database rows). You would document these methods with the #method annotation in the Class DocBlock. But keep in mind that the magic methods are always slower over having and/or calling the appropriate methods directly.
Use what you think makes most sense for your application and usecase.
You could achieve something like method overloading by checking the type of the passed parameter at runtime (PHP does not support this concept known from other languages like ADA, Java, C# or C++ e.g.):
[...]
/**
* #param User|array|integer $user
* #return array
*/
public function getCollection($user) {
// perhaps a switch-case would be better here
if ($user instanceof User) {
// do what has to be done if you passed in a User object
} else if (is_int($user) {
// do what has to be done if you passed in a user id
} else if (is_array($user)) {
// do what has to be done if you passed in an array of user ids
}
}
[...]
It sounds like you want to do function overloading, but PHP (even PHP5) does not do overloading like you would in, say Java. The Overloading section in the PHP manual is not function overloading:
Note: PHP's interpretation of
"overloading" is different than most
object oriented languages. Overloading
traditionally provides the ability to
have multiple methods with the same
name but different quantities and
types of arguments.
You might mean this:
class ArticleMapper {
public function getCollection($param) {
if (is_array($param)) { $this->getCollectionByArray($param); }
if (is_int($param)) { $this->getCollectionByInt($param); }
if (is_a($param, 'User')) { $this->getCollectionByUser($param); }
}
private function getCollectionByArray(array $param) { ... }
private function getCollectionByInt($param) { ... }
private function getCollectionByUser(User $param) { ... }
}
This seems like the a good way to do it to my eye.
I've always worry about calling methods by referencing them via strings.
Basically in my current scenario, I use static data mapper methods to create and return an array of data model objects (eg. SomeDataMapper::getAll(1234)). Models follow the Active Record design pattern. In some cases, there could be hundreds of records returned, and I don't want to put everything into memory all at once. So, I am using an Iterator to page through the records, as follows
$Iterator = new DataMapperIterator('SomeDataMapper', 'getAll', array(1234));
while ($Iterator->hasNext()) {
$data = $Iterator->next();
}
Is that a good way of doing this? Is it a bad idea to pass as strings the name of the mapper class and the method? I worry that this idea is not portable to other languages. Is this generally true for languages like Ruby and Python? If so, can anyone recommend a good alternative?
FYI, for future peoples' refernce, I call the method like this:
$method = new ReflectionMethod($className, $methodName);
$returnValue = $method->invokeArgs(null, $parameters);
This is essentially a version of the factory pattern - Using strings to create a object instance.
However, I question the design idea of using an iterator to control the paging of data - that's not really the purpose of an iterator. Unless we just have name confusion, but I'd probably prefer to see something like this.
$pager = new DataMapperPager( 'SomeDataMapper', 'someMethod', array(1234) );
$pager->setPageNum( 1 );
$pager->setPageSize( 10 );
$rows = $pager->getResults();
foreach ( $rows as $row )
{
// whatever
}
Of course, DataMapperPager::getResults() could return an iterator or whatever you'd want.
It is an acceptable way of doing it. Both Python and Ruby support it and thus should be portable. Python can do it as easily as PHP can, however Ruby has a little more to it. In Python at least, it is useful for when the particular class you're referencing has not yet been imported nor seen yet in the file (i.e. the class is found lower in the same file as where you're trying to reference it.)
Getting a class object from a string in Ruby: http://infovore.org/archives/2006/08/02/getting-a-class-object-in-ruby-from-a-string-containing-that-classes-name/
PHP doesn't really support the passing of functions any other way. All dynamic method invocation functions in PHP take what they call a "callback" - see http://us.php.net/manual/en/language.pseudo-types.php#language.types.callback for documentation on that. As you'll see, they're just string or arrays of strings in different usage patterns, so you're not far off.
There are however, design patterns that work around this. For instance, you could define a DataMapper interface that all of your mapper classes must implement. Then, instead of passing in the class and method as string, you could pass the mapper instance to your iterator and since it requires the interface it could call the interface methods directly.
pseudocode:
interface DataMapper
{
public function mapData($data);
}
class DataMapperIterator ...
{
public function __construct(DataMapper $mapper, ...)
{
...
}
...
public function next()
{
... now we can call the method explicitly because of interface ...
$this->mapper->mapData($data);
}
}
class DataMapperImplemenation implements DataMapper
{
...
public function mapData($data)
{
...
}
...
}
Calling methods by name with passed in strings isn't horrible, there's probably only a performance penalty in that the bytecode generated can't be as optimized - there will always be a symbol lookup - but I doubt you'll notice this much.