Define a class dynamically? - php

Is there a way to dynamically and conditionally create a class definition in PHP, i.e.
if (condition matches)
include file containing class definition
else
class myclass extends ancestor_class { .................... }
without eval()?
My background is the accepted answer to this question. I am looking for the best way to build a untouchable core library, with user-defined empty classes extending the core library if necessary.
I want to create the final class definition "on the fly" if there is no user-defined empty class for a certain ancestor class.
Including another include file (containing the empty class definition) is out of the question, as there will be quite a number of classes.
Update: This seems to be possible in a normal condition block - didn't think of that. However, according to this article, it also seems to be regarded as bad practice and may be removed in the future. Hmm. Creative ideas welcome.

See the comments under the question for more information -- I'll try to summarize, as it might be useful to other :-)
First of all, I suppose the goal is not having users create almost empty files by themselves -- I remember another question, I think it was from you, where that popped up.
If so, why not just create those almost empty files dynamically ?
I mean, some idea like that :
if (condition matches) {
include file containing class definition
} else {
create another file containg the alternate class definition
include that other file
}
As #Gordon said in a comment, a possibility, to makes things a bit easier, would be to use Zend_CodeGenerator, to help with that task of code-generation.
As pointed out : the users will not likely have write-access on the library directory, but nothing prevents you from creating those files into another "cache" directory -- afterall, it's likely that your application has write-access to at least one directory :
For cache ^^
Or, at least, for logs
A solution would be to use eval:
build a string containing the class' definition
evaluate it
But I would really not do that, for at least two reasons :
Debugging will probably be harder -- even for an almost empty file/string
Using a file means there is a possibility for opcode caching (APC, or similar tools)
And, as a third, thinking about it : having a file means your IDE can parse it -- might help with code-completion, type-hinting, ...
Seeing your update, I tried conditional declaration :
if (false) {
class A {
public $a = 10;
}
} else {
class A {
public $a = 20;
}
}
$a = new A();
var_dump($a);
Changing the condition from false to true and vice-versa, I get two alternate output :
object(A)[1]
public 'a' => int 20
object(A)[1]
public 'a' => int 10
So... well, seems to be working!
(I'm surprised, I thought it was only possible for functions!)
But, still, I don't really like that idea... Not sure how it behaves in IDEs -- and I like having one class per file...
(Yeah, personnal opinion)

Related

Autoloading as "mock" class in php

Lets suppose I want to auto load classes, so far thats normal. Now, lets suppose we are in "test" environment. I want to load other classes instead, this classes act just like others, but with some modifications. So, originally
class A
{
public function method()
{
return rand(1,10);
}
$a = new A(); // in the meantime autoloader finds and load class A
$a->method();
and what I want:
class Adev
{
public function method()
{
something::log ('method running');
return rand(1,10);
}
}
$a = new A(); // and then I dont need "A" class but "Adev"
$a->method();
so somewhat "renaming" method should be used, instead of refactoring the code.
use get_declared_classes and eval e.g.
$classes = get_declared_classes();
foreach ($classes as $class)
eval("\$".$class." = new ".$class."();");
Updated (and messy)
A couple of possible ways of tackling your issue - may be worth a closer look. At the bottom, there is also a personal consideration/suggestion.
The shortest of short fixes might just apply in your case: instead of having PHP's autoloader looking for the .php extension, you could set it so that, when testing your code, you are in fact looking for files ending on dev.php, so that class A, when passed as a string to the autoloarder becomes Adev.php:
if (RUNNING_ENV=== 'test')
{
spl_autoload_extensions('dev.php');
}
Not sure, but perhaps use getenv() to determine if you're running on test/dev or production environment, and based on that register different autoloaders? spl_autoload_register is a handy function for that:
function prodAutoload($class)
{
//normal loading
}
function tstAutoload($class)
{
$class .='Dev';
//add Dev to className, proceed as you normally would?
}
if (getenv('RUN_ENV') === 'prod')
{
spl_autoload_register('prodAutoload');
}
else
{
spl_autoload_register('tstAutoload');
}
Of course, there will be a bit more to it, than just these few lines. But with this approach, you don't need differently named classes: A will be loaded form either the dev or live file, based on the autoloader/extension.
That way, you can at least keep type-hinting all the way through, without any issues. Of course, maintainability will be even more of a nightmare: Edit 1 file, make sure to edit the other one, too.
That's why I must say, personally, I wouldn't go through all this trouble of writing different classes for test & live environments. At one point, you'll run into trouble with that...
Suppose you fix a bug in test, but fail to edit the production version? Or the other way 'round... I think you're better off spending a little time in setting up a decent debugger and test environment that will work with the same code, but (for example) not the actual production databases.
Useful links:
manage autoload extensions
workings of default autoloader + examples
register (multiple) custom autoloaders)
Have you considered using namespaces? The code that follows is probably not 100% correct, but the gist of it would be:
# A.php
class A {...}
# ADev.php
class ADev {...}
# script.php
use ADev as A;
$a = new A; # of class ADev
See:
http://php.net/manual/en/language.namespaces.importing.php
Have you considered a simple solution rather than a complex one?
Make Class A do what ADev does i.e. include the logging function and forget about duplicating all your classes.
Make the something:: class test a enviroment variable or a simple config variable.
So something::debug tests $DO_I_WANT_DEBUGGING_ON = TRUE, if it is then you do the logging otherwise you do not.

PHP_CodeSniffer Many Classes per file sniff

We have a standard in use, where we create exceptions within the main class for returning errors etc... The problem is, that all the standard sniffs do not like this. We are writing our own sniffs then for this, but thought I would inquire why this was not desirable?
For instance, we have:
<?php
class FOO_EXCEPTION extends Exception { }
class FOO_EXCEPTION_BAR extends FOO_EXCEPTION { }
class FOO_EXCEPTION_POLE extends FOO_EXCEPTION { }
class FOO
{
public function MethodDoingSomething()
{
if('some condition happens') {
throw new FOO_EXCEPTION_BAR();
}
if('some other condition') {
throw new FOO_EXCEPTION_POLE();
}
...
}
}
?>
This allows our code to return different exceptions to indicate what happened to the caller, but if a dedicated try/catch is not available, the basic Exception may still be caught.
This comes in handy when working with databases or other external objects, since the nature of the error may be returned to a component higher up the call stack to handle the error.
For instance, if you are deleting a file, and the file does not exist, the code may throw the exception, but the caller has the option to ignore this if it was not concerned that the file did not exist, since it was trying to delete it anyhow. However, another caller, could error out with the absence of a file that was suppose to exist when it was being deleted.
In my opinion, the coding standard which you describe in your question is perfectly reasonable. And I think for the purposes of your project it would be better to tweak the "standard multiple classes per file" sniff so that it works with your code in this particular (special) case rather than waste your time tweaking your codebase to comply with "the letter of the law" for this particular sniff.
I agree with the assertion that it is better in general to avoid putting multiple class definitions in a single file. But every argument I've read (so far) for moving each and every Exception-derived class into its own separate file strikes me as an exhortation to "improve" code by making it less readable. As a human, I gain no maintainability benefit from cluttering my code with files containing a single line, each.
It's true that it is easier to write an autoloader, for example, if each class lives in its own file. And if you're generating/compiling your PHP code from some sort of meta-language then it costs you nothing to add extra levels to your directory structure. But I reject the conclusion that this way of organizing the code actually improves it in any useful-to-humans way.
EDIT:
For the record, I can see that it would be a good idea to move the definition of an Exception-derived class into its own file if it actually contains some "testable" logic. In such cases you might need to mock/stub the class when writing automated tests for the logic which uses the class, which would require you to be able to load the class definition separately from the logic which uses it. But this not the situation described in the original question, where the Exception-derived classes are all "empty".

Abstract class - children type

I'm trying to design some class hierarchy and I got "stuck" at this part.
Lets say that I have following classes
abstract class Video
{
const TYPE_MOVIE = 1;
const TYPE_SHOW = 2;
abstract public function getTitle();
abstract public function getType();
}
class Movie extends Video
{
// ...
public function getType()
{
return self::TYPE_MOVIE;
}
}
class Show extends Video
{
// ...
public function getType()
{
return self::TYPE_SHOW;
}
}
In the diffrent part of the system I have (Parser) class that encapsulates creation of
movie and show objects and returns obj. to the client.
Question: What is the best way to get a type of a obj. returned from parser/factory class, so that client can do something like
$video = $parser->getVideo('Dumb and Dumber');
echo $video->getTitle();
// Way 1
if($video->getType == 'show') {
echo $video->getNbOfSeasons();
}
// Way 2
if($video instanceof Show) {
echo $video->getNbOfSeasons();
}
// Current way
if($video->getType == Video::TYPE_SHOW) {
echo $video->getNbOfSeasons();
}
Is there a better way than my solution (read as: does my solution suck?)?
Is there a better way than my solution (read as: does my solution suck?)?
Your solution does not suck, per se. However, whenever someone is trying to determine the subtype to perform some actions, I tend to wonder; why? This answer might be a little theoretical and perhaps even a little pedantic, but here goes.
You shouldn't care. The relationship between a parent and a child class is that the child class overwrites the behaviour of the parent. A parent class should always be substitutable by it's children, regardless which one. If you find yourself asking: how do I determine the subtype, you're usually doing one of two things "wrong":
You're attempting to perform an action based upon subtype. Normally, one would opt for moving that action to the class itself, instead of "outside" of the class. This makes for more manageable code as well.
You're attempting to fix a problem you've introduced yourself by using inheritance, where inheritance isn't warranted. If there is a parent, and there are children, each of which are to be used differently, each of which have different methods, just stop using inheritance. They're not the same type. A film is not the same a tv-serie, not even close. Sure, you can see both on your television, but the resemblance stops there.
If you're running into issue number 2, you're probably using inheritance not because it makes sense, but simply to reduce code duplication. That, in and on itself, is a good thing, but the way you're attempting to do so might not be optimal. If you can, you could use composition instead, although I have my doubts where the duplicated behaviour would be, apart from some arbitrary getters and setters.
That said, if your code works, and you're happy with it: go for it. This answer is correct in how to approach OO, but I don't know anything about the rest of your application, so the answer is generic.
I'd go with way 2. It abstracts you the need to add another constant at Video in case you may want to add class SoapOpera extends Show (for instance).
With way #2, you are less dependent on constants. Whatever information you can gain without hardcoding it, means less problems to likely happen in the future in case you want to extend. Read about Tight an Loose Coupling.
I think the second option is better, using instanceof. This is in general common to all OOP design and not just PHP.
With your first option, you have specifics about derived classes in the base class, and thus must modify the base class for each new derived class you add, which should always be avoided.
Leaving the base class as-is when adding new derived classes promotes code reuse.
If there is a "right" way, and everything is subjective in coding of course (as long as it doesn't adversely impact performance/maintainability ;) ), then it's the second way as "Truth" and "Brady" have pointed out.
The upside of doing things the way you're doing them now (class constants in the abstract) is that when you're working with other developers it can provide hints as to how you expect the abstract class to be interacted with.
For instance:
$oKillerSharkFilm = Video::factory(Video::MOVIE, 'Jaws', 'Dundundundundundun');
$oKillerSharkDocumentary = Video::factory(Video::DOCUMENTARY, 'Jaws', 'A Discovery Shark Week Special');
Of course, the downside is that you have to maintain the "allowable extensions" in the abstract class.
You could still use the instanceof method as demonstrated in your question and maintain the list of allowable extension in the abstract predominantly for control/type fixing.

Including child class requires parent class included first

I have asked a similar question to this one already but I think it was badly worded and confusing so hopefully I can make it a bit clearer.
I am programming in a native Linux file system.
I have a class of HelpTopic:
class HelpTopic extends Help{}
And a class of Help:
class Help{}
Now I go to include HelpTopic:
include('HelpTopic.php');
And even though I do not instantiate HelpTopic with new HelpTopic() PHP (in a Linux file system) still reads the class signature and tries to load Help with HelpTopic.
I do not get this behaviour from a cifs file system shared from a Windows System.
My best guess is that there is some oddity with Linux that causes PHP to react this way but not sure what.
Does anyone have any ideas or solutions to this problem?
EDIT:
I have added my loading function to show what I am doing:
public static function import($cName, $cPath = null){
if(substr($cName, -2) == "/*"){
$d_name = ROOT.'/'.substr($cName, 0, -2);
$d_files = getDirectoryFileList($d_name, array("\.php")); // Currently only accepts .php
foreach($d_files as $file){
glue::import(substr($file, 0, strrpos($file, '.')), substr($cName, 0, -2).'/'.$file);
}
}else{
if(!$cPath) $cPath = self::$_classMapper[$cName];
if(!isset(self::$_classLoaded[$cName])){
self::$_classLoaded[$cName] = true;
if($cPath[0] == "/" || preg_match("/^application/i", $cPath) > 0 || preg_match("/^glue/i", $cPath) > 0){
return include ROOT.'/'.$cPath;
}else{
return include $cPath;
}
}
return true;
}
}
I call this by doing glue::inmport('application/models/*'); and it goes through including all the models in my app. Thing is PHP on a linux based file system (not on cifs) is trying to load the parents of my classes without instantiation.
This is a pretty base function that exists in most frameworks (in fact most of this code is based off of yiis version) so I am confused why others have not run into this problem.
And even though I do not instantiate HelpTopic with new HelpTopic() PHP still reads the class signature and tries to load Help with HelpTopic.
Correct.
In order to know how to properly define a class, PHP needs to resolve any parent classes (all the way up) and any interfaces. This is done when the class is defined, not when the class is used.
You should probably review the PHP documentation on inheritance, which includes a note explaining this behavior:
Unless autoloading is used, then classes must be defined before they are used. If a class extends another, then the parent class must be declared before the child class structure. This rule applies to class that inherit other classes and interfaces.
There are two ways to resolve this problem.
First, add a require_once at the top of the file that defines the child class that includes the file defining the parent class. This is the most simple and straight-forward way, unless you have an autoloader.
The second way is to defione an autoloader. This is also covered in the documentation.
The ... thing ... you're using there is not an autoloader. In fact, it's a horrible abomination that you should purge from your codebase. It's a performance sap and you should not be using it. It also happens to be the thing at fault.
We don't have the definition of getDirectoryFileList() here, so I'll assume it uses either glob() or a DirectoryIterator. This is the source of your problem. You're getting the file list in an undefined order. Or, rather, in whatever order the underlying filesystem wants to give to you. On one machine, the filesystem is probably giving you Help.php before HelpTopic.php, while on the other machine, HelpTopic.php is seen first.
At first glance, you might think this is fixable with a simple sort, but it's not. What happens if you create a Zebra class, and then later need to create an AlbinoZebra that inherits from it? No amount of directory sorting is going to satisfy both the "load ASCIIbetical" and the "I need the Zebra to be first" requirements.
Let's also touch on the performance aspect of the problem. On every single request, you're opening a directory and reading the list of files. That's one hell of a lot of stat calls. This is slow. Very slow. Then, one by one, regardless of whether or not you'll need them, you're including the files. This means that PHP has to compile and interpret every single one of them. If you aren't using a bytecode cache, this is going to utterly destroy performance if the number of files there ever grows to a non-trivial number.
A properly constructed autoloader will entirely mitigate this problem. Autoloaders run on demand, meaning that they'll never attempt to include a file before it's actually needed. Good-performing autoloaders will know where the class file lives based on the name alone. In modern PHP, it's accepted practice to name your classes such that they'll be found easily by an autoloader, using either namespaces or underscores -- or both -- to map directory separators. (Meaning namespace \Models; class Help or class Models_Help would live in Models/Help.php)
Unfortunately most examples won't be useful here, as I don't know what kind of weird things your custom framework does. Take a peek at the Zend Framework autoloader, which uses prefix registration to point class prefixes (Model_) at directories.

Implications of Instantiating Objects with Dynamic Variables in PHP

What are the performance, security, or "other" implications of using the following form to declare a new class instance in PHP
<?php
$class_name = 'SomeClassName';
$object = new $class_name;
?>
This is a contrived example, but I've seen this form used in Factories (OOP) to avoid having a big if/switch statement.
Problems that come immediately to mind are
You lose the ability to pass arguments into a constructor (LIES. Thanks Jeremy)
Smells like eval(), with all the security concerns it brings to the table (but not necessarily the performance concerns?)
What other implications are there, or what search engine terms other than "Rank PHP Hackery" can someone use to research this?
One of the issues with the resolving at run time is that you make it really hard for the opcode caches (like APC). Still, for now, doing something like you describe in your question is a valid way if you need a certain amount of indirection when instanciating stuff.
As long as you don't do something like
$classname = 'SomeClassName';
for ($x = 0; $x < 100000; $x++){
$object = new $classname;
}
you are probably fine :-)
(my point being: Dynamically looking up a class here and then doesn't hurt. If you do it often, it will).
Also, be sure that $classname can never be set from the outside - you'd want to have some control over what exact class you will be instantiating.
It looks you can still pass arguments to the constructor, here's my test code:
<?php
class Test {
function __construct($x) {
echo $x;
}
}
$class = 'Test';
$object = new $class('test'); // echoes "test"
?>
That is what you meant, right?
So the only other problem you mentioned and that I can think of is the security of it, but it shouldn't be too difficult to make it secure, and it's obviously a lot more secure than using eval().
I would add that you can also instanciate it with a dynamic number of parameters using :
<?php
$class = "Test";
$args = array('a', 'b');
$ref = new ReflectionClass($class);
$instance = $ref->newInstanceArgs($args);
?>
But of course you add some more overhead by doing this.
About the security issue I don't think it matters much, at least it's nothing compared to eval(). In the worst case the wrong class gets instanciated, of course this is a potential security breach but much harder to exploit, and it's easy to filter using an array of allowed classes, if you really need user input to define the class name.
There may indeed be a performance hit for having to resolve the name of the variable before looking up the class definition. But, without declaring classes dynamically you have no real way to do "dyanmic" or "meta" programming. You would not be able to write code generation programs or anything like a domain-specific language construct.
We use this convention all over the place in some of the core classes of our internal framework to make the URL to controller mappings work. I have also seen it in many commercial open source applications (I'll try and dig for an example and post it). Anyway, the point of my answer is that it seems well worth what is probably a slight performance decrease if it makes more flexible, dynamic code.
The other trade-off that I should mention, though, is that performance aside, it does make the code slightly less obvious and readable unless you are very careful with your variable names. Most code is written once, and re-read and modified many times, so readability is important.
Alan, there's nothing wrong with dynamic class initialisation. This technique is present also in Java language, where one can convert string to class using Class.forClass('classname') method. It is also quite handy to defer algorithm complexity to several classes instead of having list of if-conditions. Dynamic class names are especially well suited in situations where you want your code to remain open for extension without the need for modifications.
I myself often use different classes in conjunction with database tables. In one column I keep class name that will be used to handle the record. This gives me great power of adding new types of records and handle them in unique way without changing a single byte in existing code.
You shouldn't be concerned about the performance. It has almost no overhead and objects themselves are super fast in PHP. If you need to spawn thousands of identical objects, use Flyweight design pattern to reduce memory footprint. Especially, you should not sacrifice your time as a developer just to save milliseconds on server. Also op-code optimisers work seamlessly with this technique. Scripts compiled with Zend Optimizer did not misbehave.
So I've recently encountered this, and wanted to give my thoughts on the "other" implications of using dynamic instantiation.
For one thing func_get_args() throws a bit of a wrench into things. For example I want to create a method that acts as a constructor for a specific class (e.g. a factory method). I'd need to be able to pass along the params passed to my factory method to the constructor of the class I'm instantiating.
If you do:
public function myFactoryMethod()
{
$class = 'SomeClass'; // e.g. you'd get this from a switch statement
$obj = new $class( func_get_args() );
return $obj;
}
and then call:
$factory->myFactoryMethod('foo','bar');
You're actually passing an array as the first/only param, which is the same as new SomeClass( array( 'foo', 'bar' ) ) This is obviously not what we want.
The solution (as noted by #Seldaek) requires us to convert the array into params of a constructor:
public function myFactoryMethod()
{
$class = 'SomeClass'; // e.g. you'd get this from a switch statement
$ref = new ReflectionClass( $class );
$obj = $ref->newInstanceArgs( func_get_args() );
return $obj;
}
Note: This could not be accomplished using call_user_func_array, because you can't use this approach to instantiate new objects.
HTH!
I use dynamic instantiation in my custom framework. My application controller needs to instantiate a sub-controller based on the request, and it would be simply ridiculous to use a gigantic, ever-changing switch statement to manage the loading of those controllers. As a result, I can add controller after controller to my application without having to modify the app controller to call them. As long as my URIs adhere to the conventions of my framework, the app controller can use them without having to know anything until runtime.
I'm using this framework in a production shopping cart application right now, and the performance is quite favorable, too. That being said, I'm only using the dynamic class selection in one or two spots in the whole app. I wonder in what circumstances you would need to use it frequently, and whether or not those situations are ones that are suffering from a programmer's desire to over-abstract the application (I've been guilty of this before).
One problem is that you can't address static members like that, for instance
<?php
$className = 'ClassName';
$className::someStaticMethod(); //doesn't work
?>
#coldFlame: IIRC you can use call_user_func(array($className, 'someStaticMethod') and call_user_func_array() to pass params
class Test {
function testExt() {
print 'hello from testExt :P';
}
function test2Ext()
{
print 'hi from test2Ext :)';
}
}
$class = 'Test';
$method_1 = "testExt";
$method_2 = "test2Ext";
$object = new $class(); // echoes "test"
$object->{$method_2}(); // will print 'hi from test2Ext :)'
$object->{$method_1}(); // will print 'hello from testExt :P';
this trick works in both php4 and php5 :D enjoy..

Categories