1
\$\begingroup\$

I'm trying to create small PHP framework for my own needs (and likes). However there are questions for which I may need advice of more wise and experienced people before things became too complicated - so I moved it to github from private repo.

Currently I'm curious about organizing imports of modules better. I use the special object, "context" - to hold links to all necessary modules - data access objects, utils etc. I use lazy initialization here, so that when field of this object is required, it is provided by the getter of the same name.

It looks like:

class ProtoContext {

    function __get($name) {
        $methodName = 'get' . ucfirst($name);
        if (!method_exists($this, $methodName)) {
            throw new Exception("No property '$name' in Context!");
        }
        $res = $this->$methodName();
        if (is_object($res)) {
            $res->ctx = $this;
        }
        $this->$name = $res;
        return $res;
    }

    protected function getElems() {
        return Elems::$elems;
    }

    protected function getUtil() {
        module('sys/Util');
        return new Util();
    }

}

main Context class is inherited from this ProtoContext, though it is not significant now. With the grow of application context may look like this:

module('sys/ProtoContext');

class Context extends ProtoContext {

    protected function getAuth() {
        module('MyAuth');
        return new MyAuth();
    }

    protected function getUsersDao() {
        module('dao/MysqlDao');
        return new MysqlDao('users');
    }

    protected function getRolesDao() {
        module('dao/MysqlDao');
        return new MysqlDao('roles');
    }

    /*
     * 5-10 more similar methods 'getSomethingDao'    
     * each including MysqlDao via method 'module'
     */

    protected function getLinksCViewDao() {
        module('dao/MysqlDao');
        return new MysqlDao('linksc_view');
    }

}

There is no problem that module method may be called several times, include is performed once only. However it looks annoying that inclusion of MysqlDao is mentioned so many times. On the other hand if I pull it above the class (like import of ProtoContext) it will be imported even if I need it not. For example when addressing $ctx->auth field (which will call getAuth method).

I wonder, whether here is comfortable workaround which will preserve lazy load and lazy initialization - and at the same time will allow to get rid of extra imports?

\$\endgroup\$
1
  • \$\begingroup\$ Why would you implement module method if we already have lazy loading trough autoloading? It looks to me you're doing something that's been done before (and much better). \$\endgroup\$
    – N.B.
    Aug 5 '13 at 13:21
3
\$\begingroup\$

As N.B. mentioned in their comment - why are you implementing a module method if we already have lazy loading via autoloading? I've even written a class that you can simply attach and not have to worry about loading your classes. Now, there are better autoloaders out there - like Symphony's ClassLoader/autoload.php. Anyways, here's your code, with my LazyLoader class handling the loading of methods (untested code).

/LazyLoader.php

/**
 * LazyLoader
 * A fast, strict lazy loader (time ~ 0.0001)
 * 
 * Class name must match File name.
 * If class has namespace, must be called via namespace.
 *
 * @author Juan L. Sanchez <juanleonardosanchez.com>
 * @license MIT
 * @version 1.2.0
 * @internal 06.26.2013
 */

Namespace LazyLoader;

class LazyLoader{
    public static $dirRoot;

    public static function autoload($class_name){
        $file = dirname(__FILE__) . 
                (strlen(self::$dirRoot) > 0 ? self::$dirRoot : "") . 
                '/' . array_pop(explode("\\", $class_name)) . '.php';

        file_exists($file) ? require_once($file) : "";
    }

    public static function SetBaseDirectory($directory_root){
        self::$dirRoot = substr($directory_root, -1) == "\\" ? 
                         substr($directory_root, 0, -1) : "";
    }

    public static function Register(){
        return spl_autoload_register(__NAMESPACE__ .'\LazyLoader::autoload');
    }
}

$LazyLoader = new LazyLoader;
$LazyLoader->SetBaseDirectory("Classes"); # Optional
$LazyLoader->Register();

/Context.php

<?php
module('sys/ProtoContext');

class Context extends ProtoContext {

    protected function getAuth() {
        return new MyAuth();
    }

    protected function getUsersDao() {
        return new MysqlDao('users');
    }

    protected function getRolesDao() {
        return new MysqlDao('roles');
    }

    /*
     * 5-10 more similar methods 'getSomethingDao'    
     * each including MysqlDao via method 'module'
     */

    protected function getLinksCViewDao() {
        return new MysqlDao('linksc_view');
    }

}

The directory structure of the above code would look something like:

/LazyLoader.php
/Context.php
/Classes/MyAuth.php
/Classes/ProtoContext.php
/Classes/MysqlDao.php
\$\endgroup\$
2
  • \$\begingroup\$ Thanks for sharing. is_readable might be an improvement over file_exists. \$\endgroup\$
    – James P.
    Aug 29 '13 at 6:23
  • 1
    \$\begingroup\$ is_readable tests whether a file exists and is readable, whereas file_exists tests whether a file, well, exists - but doesn't check readability. If the file is not readable, it should fail loudly in the require_once, rather than failing quietly when using is_readable and/or include. That was my line of thinking. Is there a performance difference between is_readable and file_exists? That would be surely negated by my use of require_once I'd imagine? \$\endgroup\$
    – jsanc623
    Aug 29 '13 at 14:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.