Library as Singleton in Codeigniter? - php

I've created a custom library using singleton pattern. Why by this way? because I need to be able to call my functions without $this-> reference, for example, I could execute the code below:
function foo() {
MyLibrary::instance()->foo();
}
then I could call my functions in controllers like this:
function foo();
instead of
$this->mylibrary->foo();
I am in trouble because CodeIgniter try to instantiate my library when the best way to do is "read" the static instance.
Why I need to do this?
My custom library must register "hooks" from external php files located in /plugins directory outside of application folder. Look:
/application
/plugins
/plugins/helloworld.php
This is my Loader library:
class Loader{
public static $instance;
protected $hooks;
private function __construct($params = array()){
$this->hooks= array();
$this->includePlugins();
}
public static function instance()
{
if (!self::$instance)
{
self::$instance = new Loader();
}
return self::$instance;
}
public function includePlugins()
{
include_once "/plugins/helloworld.php";
}
public function do_action($hook= "after")
{
foreach ($this->actions[$hook] as $function)
{
call_user_func_array($function, array());
}
}
public function add_action($hook, $function, $priority)
{
if ($hooks !== false)
{
$this->actions[$hook][] = $function;
}
else
{
echo "hook name is unavalaible";
}
}
}
/**
* Functions using the Singleton instance !!!
*/
function do_action($hook)
{
Loader::instance()->do_action($hook);
}
function add_action($hook, $function, $priority)
{
Loader::instance()->add_action($hook, $function, $priority);
}
No, my helloworld.php (plugin) looks like this:
add_action('right_here', 'show_message', 11);
function show_message()
{
echo "hello world!";
}
Finally, I could call my the function do_action in my controller/view in order to print the message, like this:
do_action('right_here');
Note that all my important functions are called by a global function that allow me to use the singleton instance. But I am experiencing two issues:
Codeigniter needs that my contruct be public in order to
instantiate. Here I need to take my singleton.
If I let it public, several instances will be created and the library
will no longer works as expected.

I've solved the problem :)
In order to create a singleton library you must:
Include your library at system/core/Codeigniter.php like this:
require_once("/path/to/library.php");
Make your instance return with & symbol, like this:
Singleton:
public static function &instance()
{
if (!self::$instance)
{
self::$instance = new Loader();
}
return self::$instance;
}
Ready to uses your functions as well. Also, you can include th CI
object in you library.

Related

Best way to pass/use debugging class in PHP libraries

I usually use Monolog for my PHP logging and debugging, but have found I end up with each of my classes instantiating there own Monolog\Logger, which is fine for a project with just one or two classes in it, but I want to share these classes across multiple projects using Composer etc.
To avoid having each class use its own logger I currently use the following code which simply allows me to pass an instance of Logger if its is configured and if not then the class sets up a Null logger itself :
/**
* Basic Constructor
*
* #param Logger|Null Logging instance or Null to do no logging at all
*/
public function __construct($logger = null)
{
if ($logger !== null) {
$this->logger = $logger;
} else {
$this->logger = new Logger('dummy');
$this->logger->pushHandler(new NullHandler());
}
}
private function test($var1,$var2) {
$this->logger->debug('test method called with '.$var1.' and '.$var2);
}
Is this the best way to configure debugging for my classes or is there something that is more universal/just plain better coding practice?
I have also used a method inside my classes in the past that tests if $this->debug is not null and if so then calls Logger with the data, rather than sending everything to a null Logger but this then requires multiple methods for each log level:
/**
* If debug enabled, send all passed parameters to debugger
*/
public function debug()
{
if (is_null($this->debug)) {
return;
}
$args = func_get_args();
$this->debug->debug(print_r($args, true));
}
I am not using any pre built frameworks, but i would think the same problem would still occur when using my own classes with a framework.
Use a helper class in the global namespace, that has some static functions to access.
Quick fast and dirty
class Debug {
static $logger = new Logger();
public static function debug($input) {
self::$logger->debug($input);
}
... for other functions/wrapping you wish to do
}
Add a few helper functions
function debug($input)) {
Debug::debug($input);
}
and then you're good to go.
You can then do debug('mystuff') or Debug::debug('mystuff');
A bit more complicated
By using a static wrapper class that interfaces via __callStatic you can pass all the instance queries you would do on logger as a static call to the debug class, without having to worry about "copying" all methods of the logger into your own Debug wrapper.
When you add even a few helper functions you don't have to type so much for your debug helpers.
Another advantage is you can switch out logger types as you wish. Don't feel like monolog anymore? swap it out for something else.
A full working rudimentary example: https://ideone.com/tAKBWr
<?php
class Logger {
public function debug($input) {
var_dump($input);
}
public function log($input) {
echo $input;
}
}
class Debug {
protected $logger;
protected static $_instance = NULL;
final private function __construct() {
$this->logger = new Logger();
}
final private function __clone() { }
final public static function getInstance()
{
if(null !== static::$_instance){
return static::$_instance;
}
static::$_instance = new static();
return static::$_instance;
}
public function getLogger()
{
return $this->logger;
}
static public function __callStatic($name, $args)
{
$logger = self::getInstance()->getLogger();
if(method_exists($logger, $name)) {
return $logger->{$name}(...$args);
}
}
}
function debug($input)
{
Debug::debug($input);
}
function traceLog($input)
{
Debug::log($input);
}
debug(['42','asdfasdf',33]);
traceLog("Moving to track 3");

Load a class "on-the-fly" with php

I have an existent class and I want to create a system to load "plugins" for it. Those "plugins" are created as files and then included in the file with the main class.
Now I think the main class needs to extend those little "plugins" with their own classes. The problem is that I don't know what plugins will include different users. So the extending of the classes is dynamically.
How can I extend on-the-fly a class, maybe without using eval (I didn't tested that either)?
Are you talking about __autoload?
function __autoload($class) {
require_once($class.'.php');
}
$object = new Something();
This will try to require_once(Something.php);
You can sort of do it by using PHP's magic functions. Suppose you have class A. You want an "instance" of A with some extra methods available.
class A {
public $publicA;
public function doA() {
$this->publicA = "Apple";
}
}
class B {
public $publicB;
public function doB() {
$this->publicB = "Bingo";
}
private $object;
public function __construct($object) {
$this->object = $object;
}
public function __call($name, $arguments) {
return call_user_func_array(array($this->object, $name), $arguments);
}
public function __get($name) {
return $this->object->$name;
}
public function __set($name, $value) {
$this->object->$name = $value;
}
}
$b = new B(new A);
$b->doA();
$b->doB();
echo "$b->publicA, $b->publicB";
The instance $b has the methods and properties of both A and B. This technique can be extended to aggregate functionalities from any number of classes.

How to emulate __destruct() in a static class?

I've coded a simple configuration class for my own framework.
There are simple functions like get(), set() or loadFile().
But all functions and variables are static.
And now I want to implement an autosave mechanism. I had the idea to create an instance (in my init() function) whose __destruct() will call the static destruct() function:
<?php
class Config
{
static private $autoSave;
static public function get() {} /* set(), save(), load(), etc. */
static public function init($autoSave)
{
self::$autoSave = $autoSave;
new Config();
}
static public function destruct()
{
if (self::$autoSave)
self::save();
}
public function __destruct()
{
Config::destruct();
}
}
?>
Are there any better solutions or is my design pattern completely wrong in this case?
Are there any better solutions or is my design pattern completely wrong in this case?
Destructors are called on objects only, not for static classes.
Instead you could convert your class from static to regular so you can create an instance of it. Then it will have the destructor. Additionally it makes your code easier to re-use and test.
Additionally you're able to implement magic methods for __get and __set or ArrayAccess which often is useful for easy data storage and access as for a configuration.
Alternatively, you can add a destructor object to a static class member to achieve what you're looking for:
class ConfigDestructor
{
public function __destruct()
{
Config::destruct();
}
}
class Config
{
static private $destructorInstance;
static private $autoSave;
static public function get() {} /* set(), save(), load(), etc. */
static public function init($autoSave)
{
if (null === self::$destructorInstance)
self::$destructorInstance = new ConfigDestructor();
self::$autoSave = $autoSave;
}
static public function destruct()
{
if (self::$autoSave)
self::save();
}
}
Just FYI: You wrote you want to add an auto-save functionality. There is a common gap to fall over for both __destruct() and register_shutdown_function:
Note: Working directory of the script can change inside the shutdown function under some web servers, e.g. Apache.
You should specify an absolute path to access the file you want to save into. See as well: PHP file creation/write within destructor.
Inside your init method, add a call to register_shutdown_function:
register_shutdown_function(array('Config', 'destruct'));
Have you looked at register_shutdown_function? You could add your method to the shutdown part of the script.
It could also be worth it to look at the Singleton pattern.
You can create an instance of this static class on autoregister.
$instance = array();
spl_autoload_register(function ($class)
{
...
global $instance;
if ($isStatic) $instance[] = new $class();
...
});
This is working fine for me.
... and for those who don't like readable code (it is untested):
class staticInstances()
{
private static $list = array();
public static function add($class)
{
self::$list[] = new $class();
}
function __distruct()
{
foreach (self::$list as $class)
unset(self::$list);
}
}
$staticInstances = new staticInstances();
spl_autoload_register(function ($class)
{
...
if ($isStatic) staticInstances::add($class);
...
});

How to add a method to an existing class in PHP?

I'm using WordPress as a CMS, and I want to extend one of its classes without having to inherit from another class; i.e. I simply want to "add" more methods to that class:
class A {
function do_a() {
echo 'a';
}
}
then:
function insert_this_function_into_class_A() {
echo 'b';
}
(some way of inserting the latter into A class)
and:
A::insert_this_function_into_class_A(); # b
Is this even possible in tenacious PHP?
If you only need to access the Public API of the class, you can use a Decorator:
class SomeClassDecorator
{
protected $_instance;
public function myMethod() {
return strtoupper( $this->_instance->someMethod() );
}
public function __construct(SomeClass $instance) {
$this->_instance = $instance;
}
public function __call($method, $args) {
return call_user_func_array(array($this->_instance, $method), $args);
}
public function __get($key) {
return $this->_instance->$key;
}
public function __set($key, $val) {
return $this->_instance->$key = $val;
}
// can implement additional (magic) methods here ...
}
Then wrap the instance of SomeClass:
$decorator = new SomeClassDecorator(new SomeClass);
$decorator->foo = 'bar'; // sets $foo in SomeClass instance
echo $decorator->foo; // returns 'bar'
echo $decorator->someMethod(); // forwards call to SomeClass instance
echo $decorator->myMethod(); // calls my custom methods in Decorator
If you need to have access to the protected API, you have to use inheritance. If you need to access the private API, you have to modify the class files. While the inheritance approach is fine, modifiying the class files might get you into trouble when updating (you will lose any patches made). But both is more feasible than using runkit.
An updated way for 2014 that copes with scope.
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
Eg:
class stdObject {
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
}
$obj = new stdObject();
$obj->test = function() {
echo "<pre>" . print_r($this, true) . "</pre>";
};
$obj->test();
You can use the runkit extension for this, but you should really consider regular inheritance instead.
See runkit_method_add.
No you can't dynamically change a class during runtime in PHP.
You can accomplish this by either extending the class using regular inheritance:
class Fancy extends NotSoFancy
{
public function whatMakesItFancy() //can also be private/protected of course
{
//
}
}
Or you could edit the Wordpress source files.
I'd prefer the inheritance way. It's a lot easier to handle in the long run.

Faking method attributes in PHP?

Is it possible to use the equivalent for .NET method attributes in PHP, or in some way simulate these?
Context
We have an in-house URL routing class that we like a lot. The way it works today is that we first have to register all the routes with a central route manager, like so:
$oRouteManager->RegisterRoute('admin/test/', array('CAdmin', 'SomeMethod'));
$oRouteManager->RegisterRoute('admin/foo/', array('CAdmin', 'SomeOtherMethod'));
$oRouteManager->RegisterRoute('test/', array('CTest', 'SomeMethod'));
Whenever a route is encountered, the callback method (in the cases above they are static class methods) is called. However, this separates the route from the method, at least in code.
I am looking for some method to put the route closer to the method, as you could have done in C#:
<Route Path="admin/test/">
public static void SomeMethod() { /* implementation */ }
My options as I see them now, are either to create some sort of phpDoc extension that allows me to something like this:
/**
* #route admin/test/
*/
public static function SomeMethod() { /* implementation */ }
But that would require writing/reusing a parser for phpDoc, and will most likely be rather slow.
The other option would be to separate each route into it's own class, and have methods like the following:
class CAdminTest extends CRoute
{
public static function Invoke() { /* implementation */ }
public static function GetRoute() { return "admin/test/"; }
}
However, this would still require registering every single class, and there would be a great number of classes like this (not to mention the amount of extra code).
So what are my options here? What would be the best way to keep the route close to the method it invokes?
This is how I ended up solving this. The article provided by Kevin was a huge help. By using ReflectionClass and ReflectionMethod::getDocComment, I can walk through the phpDoc comments very easily. A small regular expression finds any #route, and is registered to the method.
Reflection is not that quick (in our case, about 2,5 times as slow as having hard-coded calls to RegiserRoute in a separate function), and since we have a lot of routes, we had to cache the finished list of routes in Memcached, so reflection is unnecessary on every page load. In total we ended up going from taking 7ms to register the routes to 1,7ms on average when cached (reflection on every page load used 18ms on average.
The code to do this, which can be overridden in a subclass if you need manual registration, is as follows:
public static function RegisterRoutes()
{
$sClass = get_called_class(); // unavailable in PHP < 5.3.0
$rflClass = new ReflectionClass($sClass);
foreach ($rflClass->getMethods() as $rflMethod)
{
$sComment = $rflMethod->getDocComment();
if (preg_match_all('%^\s*\*\s*#route\s+(?P<route>/?(?:[a-z0-9]+/?)+)\s*$%im', $sComment, $result, PREG_PATTERN_ORDER))
{
foreach ($result[1] as $sRoute)
{
$sMethod = $rflMethod->GetName();
$oRouteManager->RegisterRoute($sRoute, array($sClass, $sMethod));
}
}
}
}
Thanks to everyone for pointing me in the right direction, there were lots of good suggestions here! We went with this approach simply because it allows us to keep the route close to the code it invokes:
class CSomeRoutable extends CRoutable
{
/**
* #route /foo/bar
* #route /for/baz
*/
public static function SomeRoute($SomeUnsafeParameter)
{
// this is accessible through two different routes
echo (int)$SomeUnsafeParameter;
}
}
Using PHP 5.3, you could use closures or "Anonymous functions" to tie the code to the route.
For example:
<?php
class Router
{
protected $routes;
public function __construct(){
$this->routes = array();
}
public function RegisterRoute($route, $callback) {
$this->routes[$route] = $callback;
}
public function CallRoute($route)
{
if(array_key_exists($route, $this->routes)) {
$this->routes[$route]();
}
}
}
$router = new Router();
$router->RegisterRoute('admin/test/', function() {
echo "Somebody called the Admin Test thingie!";
});
$router->CallRoute('admin/test/');
// Outputs: Somebody called the Admin Test thingie!
?>
Here's a method which may suit your needs. Each class that contains routes must implement an interface and then later loop through all defined classes which implement that interface to collect a list of routes. The interface contains a single method which expects an array of UrlRoute objects to be returned. These are then registered using your existing URL routing class.
Edit: I was just thinking, the UrlRoute class should probably also contain a field for ClassName. Then $oRouteManager->RegisterRoute($urlRoute->route, array($className, $urlRoute->method)) could be simplified to $oRouteManager->RegisterRoute($urlRoute). However, this would require a change to your existing framework...
interface IUrlRoute
{
public static function GetRoutes();
}
class UrlRoute
{
var $route;
var $method;
public function __construct($route, $method)
{
$this->route = $route;
$this->method = $method;
}
}
class Page1 implements IUrlRoute
{
public static function GetRoutes()
{
return array(
new UrlRoute('page1/test/', 'test')
);
}
public function test()
{
}
}
class Page2 implements IUrlRoute
{
public static function GetRoutes()
{
return array(
new UrlRoute('page2/someroute/', 'test3'),
new UrlRoute('page2/anotherpage/', 'anotherpage')
);
}
public function test3()
{
}
public function anotherpage()
{
}
}
$classes = get_declared_classes();
foreach($classes as $className)
{
$c = new ReflectionClass($className);
if( $c->implementsInterface('IUrlRoute') )
{
$fnRoute = $c->getMethod('GetRoutes');
$listRoutes = $fnRoute->invoke(null);
foreach($listRoutes as $urlRoute)
{
$oRouteManager->RegisterRoute($urlRoute->route, array($className, $urlRoute->method));
}
}
}
I'd use a combination of interfaces and a singleton class to register routes on the fly.
I would use a convention of naming the router classes like FirstRouter, SecondRouter and so on. This would enable this to work:
foreach (get_declared_classes() as $class) {
if (preg_match('/Router$/',$class)) {
new $class;
}
}
That would register all declared classes with my router manager.
This is the code to call the route method
$rm = routemgr::getInstance()->route('test/test');
A router method would look like this
static public function testRoute() {
if (self::$register) {
return 'test/test'; // path
}
echo "testRoute\n";
}
The interfaces
interface getroutes {
public function getRoutes();
}
interface router extends getroutes {
public function route($path);
public function match($path);
}
interface routes {
public function getPath();
public function getMethod();
}
And this is my definition av a route
class route implements routes {
public function getPath() {
return $this->path;
}
public function setPath($path) {
$this->path = $path;
}
public function getMethod() {
return $this->method;
}
public function setMethod($class,$method) {
$this->method = array($class,$method);
return $this;
}
public function __construct($path,$method) {
$this->path = $path;
$this->method = $method;
}
}
The Router manager
class routemgr implements router {
private $routes;
static private $instance;
private function __construct() {
}
static public function getInstance() {
if (!(self::$instance instanceof routemgr)) {
self::$instance = new routemgr();
}
return self::$instance;
}
public function addRoute($object) {
$this->routes[] = $object;
}
public function route($path) {
foreach ($this->routes as $router) {
if ($router->match($path)) {
$router->route($path);
}
}
}
public function match($path) {
foreach ($this->routes as $router) {
if ($router->match($path)) {
return true;
}
}
}
public function getRoutes() {
foreach ($this->routes as $router) {
foreach ($router->getRoutes() as $route) {
$total[] = $route;
}
}
return $total;
}
}
And the self register super class
class selfregister implements router {
private $routes;
static protected $register = true;
public function getRoutes() {
return $this->routes;
}
public function __construct() {
self::$register = true;
foreach (get_class_methods(get_class($this)) as $name) {
if (preg_match('/Route$/',$name)) {
$path = call_user_method($name, $this);
if ($path) {
$this->routes[] = new route($path,array(get_class($this),$name));
}
}
}
self::$register = false;
routemgr::getInstance()->addRoute($this);
}
public function route($path) {
foreach ($this->routes as $route) {
if ($route->getPath() == $path) {
call_user_func($route->getMethod());
}
}
}
public function match($path) {
foreach ($this->routes as $route) {
if ($route->getPath() == $path) {
return true;
}
}
}
}
And finally the self registering router class
class aRouter extends selfregister {
static public function testRoute() {
if (self::$register) {
return 'test/test';
}
echo "testRoute\n";
}
static public function test2Route() {
if (self::$register) {
return 'test2/test';
}
echo "test2Route\n";
}
}
the closest you can put your path to the function definition (IMHO) is right before the class definition. so you would have
$oRouteManager->RegisterRoute('test/', array('CTest', 'SomeMethod'));
class CTest {
public static function SomeMethod() {}
}
and
$oRouteManager->RegisterRoute('admin/test/', array('CAdmin', 'SomeMethod'));
$oRouteManager->RegisterRoute('admin/foo/', array('CAdmin', 'SomeOtherMethod'));
class CAdmin {
public static function SomeMethod() {}
public static function SomeOtherMethod() {}
}
There is a proposal for this, it was declined. See the rfc here:
Attributes RFC at php.net
My solution to this desire is something like this:
abstract class MyAttributableBase{
protected static $_methodAttributes=[];
public static function getMethodAtributes(string $method):?array{
if( isset(self::$_methodAttributes[$method])){
return self::$_methodAttributes[$method];
}
return null;
}
protected static function setMethodAttributes(string $method, array $attrs):void{
self::$_methodAttributes[$method] = $attrs;
}
}
class MyController extends MyAttributableBase{
protected static function getMethodAtributes(string $method):?array{
switch( $method ){
case 'myAction':
return ['attrOne'=>'value1'];
default:
return parent::getMethodAttributes($method);
}
}
}
Usage:
$c = new MyController();
print $c::getMethodAttributes('myAction')['attrOne'];
You can of course use it from within a base class method to do "routing" stuff in this case, or from a routing class that operates on "MyAttributableBase" objects, or anywhere else you would want to inspect this attached metadata for any purpose. I prefer this "in-code" solution to using phpDoc. Note I didn't attempt to test this exact code but it is mentally copied from a working solution. If it doesn't compile for some small reason it should be easy to fix and use. I have not figured out a way to cleanly put the attributes near the method definition. Using this implementation in the base you COULD set the attributes within the method (myAction in this case) as the first code to execute, but it would not be a static attribute, it would get reset at each invocation. You could add code to additionally ensure it is only set once but that's just extra code to execute and maybe is not better. Overriding the get method allows you to set the info once and refer to it once, even though it's not that close to the method definition. Keeping the static array in the base does allow some flexibility if there are cases for adding or changing metadata at runtime. I could be possible to use something like phpDoc and a static constructor to parse that when the first class is created to populate the static metadata array. I haven't found a solution that is awesome but the one I'm using is adequate.

Categories