this question is strongly related to yesterdays one.
What I want to achieve: One perfectly object-oriented plugin-system in php where I can access the plugins this way:
$plugins->testplugin->dosomethingcool();
What I've got: With some help, I managed to solve first basic problems. The first problem, I thought about was, that with this structure, a non existant plugin would blow the whole system with a fatal error. So I have to handle these errors. So actually, I have this class:
<?php
class pluginmanager {
// Simplyfied everything here...
// Basically this class catches the fatal error for calling an object, that has not been defined
public function __get($name) {
if (!isset($this->$name)) {
try{
throw new Exception(...);
}
catch(Exception $e){
// Do something with the exception
}
// Make sure to handle a "Call to member function a non-object"-error.
// The object beneath will use __call() for the error catch.
$this->$name= new DummyPlugin();
}
return $this->$name;
}
}
?>
Until now, that will work and allow me to have the object-based plugin-manager. I now realized, that for instance the possible plugin "blog" will have to use the other plugin "database-layer". Are there ways, to make this possible?
I already thought of:
make the plugin-object globally available. Two concerns - 1) bad practice (not for no reasons...) and 2) not really safe and performance-oriented.
make the plugin call a sub-plugin. This really is performance kill - but could it work?
hooking-system or else: not sweet enough... ;-)
Couldn't I make a dependency-check after all the plugins are loaded and give each plugin a reference of all the plugins it relys on? Would this reference be a complete copy or would it stay a reference in the sense of the word?
Thanks for your thoughts!
Related
I am useing a class in my code from the base framework. But it might not be available yet:
use BaseFramework\Libs\SpecialException;
So this use-Statement will result in an error. I.e. for frameworks, where this SpecialException is not available I would like to do:
use Exception as SpecialException;
so that I do not need to change my code.
I learned that the use is only creating an alias to the full named class.
I would like to use the originial SpecialException, if this is not possible I would like to use Exception.
I am wondering, what is the best practice or recommended way in PHP to solve this?
You can decide which one to throw using class_exists, it's going to be pretty nasty to actually use though.
Example:
try {
// do something
} catch (\Exception $e) {
// you'd still need to catch a common exception to all your custom types
if (class_exists('SomeCustomException')) {
throw new SomeCustomException; // or whatever
}
}
But you'd need to do that or something equally awful everywhere.
Your question suggests the actual answer here is to implement your own custom exception and throw that instead, as you have full control over it then.
Sometimes frameworks get around this kind of issue by having shared interoperability packages, so they can conform to common interfaces, throw the same exceptions and so on.
Since SpecialException might contains methods, variables and stuff that Exception doesn't contain, there is no rock-solid way to achieve what you need. Just replacing a class with a more generic one, might lead to trouble once you use some of the more dedicated methods.
You can see this post for working with class-aliases to achieve your desired behaviour, but for the reason meantioned above I wouldn't recommend it:
Why use class alisases?
You rather should use the factory-Pattern, just import the super-type of your eventually-custom-class and work with that super-type.
As soon, as you need to call a method on an instance, where you are not sure if that method is present (due to up-casting) - your class definition (or at least the method required) is placed into the wrong level inside the inheritance tree.
OK, thanks to some clues by #dognose and #bcmcfc this works for me:
use BaseFramework\Libs\SpecialException;
if (!class_exists("SpecialException")) {
class_alias("Exception", "SpecialException");
}
Why not just extend Exception? Something like this ...
namespace ProjectName\Exceptions\SpecialException;
class SpecialException extends Exception
{
// Implement custom properties and methods if required. Optional.
}
Here we have a custom class that uses SpecialException:
use \ProjectName\Exceptions\SpecialException;
class DocumentRepository
{
public static function fetchByID($docID)
{
throw new SpecialException("Document does not exist");
}
}
And now you don't need to worry about whether or not SpecialException exists or not. If calling code throws a regular Exception it will get caught, but if it throws a SpecialException it will still get caught as the new exceptions base class is Exception.
try
{
$doc = DocumentRepository::fetchByID(12);
}
catch(Exception $e)
{
die($e->getMessage());
}
Or, if you want to catch the SpecialException you can do (and I highly recommend this):
try
{
$doc = DocumentRepository::fetchByID(12);
}
catch(SpecialException $e)
{
die($e->getMessage());
}
Update to answer the problem in your comment
As a developer using a framework you have a location where you store your custom classes, files etc. right? Let me assume that this location is ProjectName/lib. And lets assume the framework you're using lives in the directory ProjectName/BaseFramework.
Your custom SpecialException will live in ProjectName/lib/Exceptions/SpecialException.php.
Currently, the framework doesn't include this exception. So in the files you wish to use SpecialException you use the following use line:
use \ProjectName\Exceptions\SpecialException
When the framework finally does implement this SpecialException you simply replace that use line with this one:
use \BaseProject\Exceptions\SpecialException
It's as simple as that.
If you try to do this in the way other users have suggested you will have dead code in your system. When SpecialException is finally implemented the checks on which type of Exception to use will be redundant.
This assumes you're using something like composer or something else that handles autoloading.
Summary
I have a class that uses debug_backtrace() for reporting where a method was called. The method may be called in third party code either directly or via some helper method that's in the same package as the class in question. In the latter case the method will report the line from the third party code and not from the helper class, thus the index at which the actual caller is in the results varies, so I'd like to include a test in my PHPUnit suite which would ensure that the correct index is found in each use case.
Is there a way I could automatically and reliably figure out the correct line in the test file that my method should return?
tl;dr, code plz
Here's some code to demonstrate. This is just the most bare bones example of the situation, please disregard that it doesn't really make sense. The actual use case is more complex and it's very much possible, likely even, that at some point in time I will mess something up and the traces end up giving wrong results. I'd like my tests to catch that.
Classes.php:
<?php
class Tracer
{
public function send()
{
$trace = debug_backtrace();
if ($trace[1]['class'] === 'Helper') {
$calledOnLine = $trace[2]['line'];
} else {
$calledOnLine = $trace[1]['line'];
}
return $calledOnLine;
}
}
class Helper
{
public static function send()
{
$Tracer = new Tracer;
return $tracer->send();
}
}
TracerTest.php:
<?php
class TracerTest extends PHPUnit_Framework_TestCase
{
public function testGetProperCallerInfo()
{
$Tracer = new Tracer;
$result1 = $Tracer->send(); // $TargetLine1
$result2 = Helper::send(); // $TargetLine2
$this->assertEquals($TargetLine1, $result1);
$this->assertEquals($TargetLine2, $result2);
}
}
Ideas
I could hardcode the line numbers into the test, and then update them each and every time something higher up in the test file changes. Even if I separate this particular test to its own file it's still going to break at some point, and besides it's such an ugly solution I wouldn't be able to look myself in the eye anymore.
Another method might be to tag the target lines with comments, load the test file to an array with file() and find the tags in the array. This is still somewhat fragile, and not really all that elegant.
Ask yourself, what is the behavior you are trying to test?
If you want to ensure you are generating exact strings, then maybe test fixtures is a good idea.
Probably more easier to maintain is to assert parts of the string which can be easily inferred.
Alternatively, you can mock the function call itself with libraries such as
https://github.com/instaclick/ICBaseTestBundle/blob/master/Test/Helper/Unit/FunctionHelper.php
http://www.workhabit.com/labs/mock-function-testing-drupal
Since I haven't been able to find a better solution, I ended up putting together a small package that let's me find lines reliably from the test source.
Accepted answer is still up for grabs if you can come up with a better solution!
In case someone is interested, the package I made is here.
I’m currently building a CMS and I have been trying to figure this out forever now.
So, I’m building this CMS so that people may make Themes, Widgets. etc for it.
My problem is, that there is functionality within the CMS core that i do not want developers to have access to, and whole classes that I don’t want them to be able to use.
An example of a function I do not want developers to have access to would be the createNewUser function.
A class i don’t want developers to have access to is the database class.
I have thought about using db_backtrace at the top of every function I want to keep "private to the CMS core", but this seems very unwise.
Any solution that involves passing an extra parameter to the function is undesirable as there is already, and will be more, function I need to keep private to the core, and this type of solution would most likely be easy to spoof.
Any help would be greatly appreciated. :)
-edit-
I’m not sure I’m making myself clear.
I will indeed be hosting it myself, although I would like to allow developers to host it locally them self allowing for local development.
I will have no problems knowing what function/classes the developers should have access to, what i’m having problems figuring out is how to implement it; Allowing one part of the system(The Core) to call a function, but not allowing a different part(The Theme).
Say I have a class
class DataBase {
public static function doSomething() {
//Stuff
}
}
How do I make doSomething visible for the core, but not for files included, that I consider not to be the a part of the core.
You can use debug_backtrace() function to get caller information. Something like:
class DataBase {
public static function doSomething() {
$allowed = false;
foreach(debug_backtrace() as $trace) {
switch($trace['class']) {
case 'Core':
case 'AnotherAllowedClass':
$allowed = true;
break 2;
}
}
if(!$allowed)
throw new Exception('denied');
/* do stuff */
}
}
}
My code is located here: https://github.com/maniator/SmallFry
Should I make it so that that the App class does not have to use static functions but at the same time be able to set and set variables for the app from anywhere?
Or should I keep it how it is now with App::get and App::set methods?
What are the advantages and disadvantages of both?
How would I accomplish that 1st task if I was to undertake it?
Related Question
Sample code:
//DEFAULT TEMPLATE
App::set('APP_NAME', 'SmallVC');
//END DEFAULT TEMPLAT
//
//DEFAULT TEMPLATE
App::set('DEFAULT_TEMPLATE', 'default');
//END DEFAULT TEMPLATE
//DEFAULT TITLE
App::set('DEFAULT_TITLE', 'Small-VC');
//END DEFAULT TITLE
//LOGIN SEED
App::set('LOGIN_SEED', "lijfg98u5;jfd7hyf");
//END LOGIN SEED
App::set('DEFAULT_CONTROLLER', 'AppController');
if(App::get('view')){
$template_file = $cwd.'/../view/'.App::get('view').'/'.App::get('method').'.stp';
if(is_file($template_file)){
include $template_file;
}
else {
include $cwd.'/../view/missingview.stp'; //no such view error
}
}
else {
App::set('template', 'blank');
include $cwd.'/../view/missingfunction.stp'; //no such function error
}
I think you have a feeling that static is bad. What I am posting may seem fairly crazy as it is a massive change. At the very least hopefully it presents a different idea of the world.
Miško Hevery wrote static methods are a death to testability.
I like testing, so for that reason I don't use them. So, how else can we solve the problem? I like to solve it using what I think is a type of dependency injection. Martin Fowler has a good but complicated article on it here.
For each object at construction I pass the objects that are required for them to operate. From your code I would make AppController become:
class AppController
{
protected $setup;
public function __construct(array $setup = array())
{
$setup += array('App' => NULL, 'Database' => NULL);
if (!$setup['App'] instanceof App)
{
if (NULL !== $setup['App'])
{
throw new InvalidArgumentException('Not an App.');
}
$setup['App'] = new App();
}
// Same for Database.
// Avoid doing any more in the constructor if possible.
$this->setup = $setup;
}
public function otherFunction()
{
echo $this->setup['App']->get('view');
}
}
The dependancies default to values that are most likely (your default constructions in the if statements). So, normally you don't need to pass a setup. However, when you are testing or want different functionality you can pass in mocks or different classes (that derive from the right base class). You can use interfaces as an option too.
Edit The more pure form of dependency injection involves further change. It requires that you pass always pass required objects rather than letting the class default one when the object isn't passed. I have been through a similar change in my codebase of +20K LOC. Having implemented it, I see many benefits to going the whole way. Objects encapsulation is greatly improved. It makes you feel like you have real objects rather than every bit of code relying on something else.
Throwing exceptions when you don't inject all of the dependencies causes you to fix things quickly. With a good system wide exception handler set with set_exception_handler in some bootstrap code you will easily see your exceptions and can fix each one quickly. The code then becomes simpler in the AppController with the check in the constructor becoming:
if (!$setup['App'] instanceof App)
{
throw new InvalidArgumentException('Not an App.');
}
With every class you then write all objects would be constructed upon initialisation. Also, with each construction of an object you would pass down the dependencies that are required (or let the default ones you provide) be instantiated. (You will notice when you forget to do this because you will have to rewrite your code to take out dependencies before you can test it.)
It seems like a lot of work, but the classes reflect the real world closer and testing becomes a breeze. You can also see the dependencies you have in your code easily in the constructor.
Well, if it was me, I would have the end goal of injecting the App dependency into any class (or class tree) that needs it. That way in testing or reusing the code you can inject whatever you want.
Note I said reuse there. That's because it's hard to re-use code that has static calls in it. That's because it's tied to the global state so you can't really "change" the state for a subrequest (or whatever you want to do).
Now, on to the question at hand. It appears that you have a legacy codebase, which will complicate things. The way I would approach it is as follows:
Create a non-static version of the app class (name it something different for now) that does nothing but proxy its get/set calls to the real app class. So, for example:
class AppProxy {
public function set($value) {
return App::set($value);
}
}
For now, all it has to do is proxy. Once we finish getting all the code talking to the proxy instead of the static app, we'll make it actually function. But until then, this will keep the application running. That way you can take your time implementing these steps and don't need to do it all in one big sweep.
Pick a main class (one that does a lot for the application, or is important) that you easily control the instantiation of. Preferably one that you instantiate in only one place (in the bootstrap is the easiest). Change that class to use Dependency Injection via the constructor to get the "appproxy".
a. Test this!
Pick another class tree to work on, based on what you think will be most important and easiest.
a. Test!!!
If you have more calls to App::, Go to #3
Change the existing App class to be non-static.
a. Test!!!!!!!!!!
Remove the AppProxy and replace with App in the dependency injectors. If you did it right, you should only have one place to change to make this switch.
Pat yourself on the back and go get a drink, cause you're done.
The reason that I segmented it out like this is that once a step is completed (any step), you can still ship working software. So this conversion could take literally months (depending on the size of your codebase) without interrupting business as usual...
Now, once you're done, you do get some significant benefits:
Easy to test since you can just create a new App object to inject (or mock it as needed).
Side effects are easier to see since the App object is required wherever it could be changed.
It's easier to componentize libraries this way since their side effects are localized/
It's easier to override (polymorphism) the core app class if it's injected than if it's static.
I could go on, but I think it's pretty easy to find resources on why statics are generally bad. So that's the approach I would use to migrate away from a static class to an instance...
If you don't want to have static functions but global access from everywhere WITHOUT passing the object to the places where it is actually needed then you pretty much can only use one thing:
A global variable
So you are not really better of doing that. But that is the only thing i can think of that would fulfill your requirements.
If you App object is something like an application config a first possible step would be to pass it to the objects that need it:
class Login {
public function __construct() {
$this->_login_seed = App::get('LOGIN_SEED');
self::$_ms = Database::getConnection();
}
changes into:
class Login {
public function __construct(App $app) {
$this->_login_seed = $app->get('LOGIN_SEED');
self::$_ms = Database::getConnection();
}
I have run into an interesting dilema. In a DataMapper class, I am generating a class name to be used for returned rows from a database.
The thing is, all of my classes are autoloaded, and can come from many places (library, application/models, etc.) and I wanted to check if the class name generated actually exists. Now, one would think that:
try
{
$test = new $className();
}
catch(Exception $ex)
{
// Class could not be loaded
}
But of course, php errors (instead of throwing an exception) saying the class could not be found... Not very helpful. Short of rewriting the autoloader in Zend_Loader to search all directories to see if the class could be loaded, is there anyway to accomplish this?
For anyone wondering why I would need to do this instead of just letting the Class Not Found error show up, if the class isn't found, I want to generate a class in a pre-determined location to make my life easy as this project goes along.
Thanks in advance!
Amy
P.S. Let me know if you guys need any more info.
PHP's function class_exists() has a flag to trigger the autoloader if the class should not be loaded yet:
http://www.php.net/class_exists
So you simply write
if (!class_exists($className)) {
// generate the class here
}