Page 1 of 2
Organizing classes with __autoload()
Posted: Thu Aug 03, 2006 12:36 pm
by Benjamin
I'm using an autoloader to load classes. I wrote this small class to display certain results. My question is this. If I want to run validation on the $answer variable, should I:
1. Create a new class in a different file and use the autoloader?
2. Put the validation class in the exampleClass file?
3. Just put a function in the exampleClass class to do the validation?
4. Perform the validation in the class that calls this class?
5. Other?
Code: Select all
<?php
class exampleClass
{
private $cache = null;
private $lf = "\n";
function __construct()
{
$this->tableHead();
}
public function addAnswer($question, $answer)
{
$this->cache .= '<tr>' . $this->lf
. ' <th>' . $question . '</th>' . $this->lf
. ' <td>' . $answer . '</td>' . $this->lf
. '</tr>';
}
public function getAnswers()
{
$this->tableFooter();
return $this->cache;
}
private function tableHead()
{
$this->cache .= '<table cellpadding="0" cellspacing="0" border="0">' . $this->lf;
}
private function tableFooter()
{
$this->cache .= '</table>' . $this->lf;
}
}
?>
Posted: Thu Aug 03, 2006 12:42 pm
by Christopher
That looks like a display/presentation class -- what often gets called the View. Usually you don't do validation there, but instead do it in you in your Model or up front in the Controller
Posted: Thu Aug 03, 2006 12:46 pm
by Benjamin
Well I've gotten myself to this point by teaching myself. I have 12 pounds of books that should arrive tomorrow though
I'm trying to minimize the number of files I create. If I call the validation from the controller, where would I put the class? In a seperate file? It's only relevent to this class.
I'm just being nit-picky so that I build a good foundation to improve my skills from.
Posted: Thu Aug 03, 2006 1:28 pm
by Christopher
astions wrote:I'm trying to minimize the number of files I create.
That may be a goal for data, but not for code. More smaller and simpler classes is generally a better direction to go than fewer bigger and more complex classes. Seek a good design and don't count files. The number "several dozen" was recently mentioned as pretty standard for the number of files just for the base framework.
astions wrote:If I call the validation from the controller, where would I put the class? In a seperate file? It's only relevent to this class.
I'm just being nit-picky so that I build a good foundation to improve my skills from.
If it is a generic Validator then put it in its own file. If it is domain specific then add methods to your Model class.
Posted: Thu Aug 03, 2006 1:31 pm
by Benjamin
Ok, sounds good. Thank you for your knowledge.
Posted: Thu Aug 03, 2006 1:35 pm
by feyd
For myself, I use the __autoload() to generate stub classes and a ServiceLocator to actually load and procure real classes. The stubs created by __autoload() log how and where they are used so I can make corrections.
Posted: Thu Aug 03, 2006 1:39 pm
by Christopher
feyd wrote:For myself, I use the __autoload() to generate stub classes and a ServiceLocator to actually load and procure real classes. The stubs created by __autoload() log how and where they are used so I can make corrections.
Very, very cool idea feyd ... thanks for sharing.

Posted: Thu Aug 03, 2006 1:57 pm
by Benjamin
feyd wrote:For myself, I use the __autoload() to generate stub classes and a ServiceLocator to actually load and procure real classes. The stubs created by __autoload() log how and where they are used so I can make corrections.
Doesn't that create a lot of overhead? The reason I like __autoload() is because I don't have to include "everything" that might be required, and each page can dynamically call what it needs. If your having each stub log an event and then include other files, that sounds like a lot of disk i/o for every page request.
Posted: Thu Aug 03, 2006 2:36 pm
by feyd
astions wrote:Doesn't that create a lot of overhead? The reason I like __autoload() is because I don't have to include "everything" that might be required, and each page can dynamically call what it needs. If your having each stub log an event and then include other files, that sounds like a lot of disk i/o for every page request.
The overhead is most often only during development as I cannot guarantee whether another __autoload() is already defined or not, nor do I want to dictate that people use my __autoload(). When my __autoload() is active it accesses the ServiceLocator checking for the existance of the class. If not found, it generates the stub and logs the class not being defined. When found, it activates the ServiceLocator to do the loading and logs that X file used Y class.
This gives me documentation of the coupling, but also gives me information of where I could place ServiceLocator calls to load a class giving me a more optimal loading pattern. My ServiceLocator supports loading whole trees of classes too though when its more straight forward to load all storage classes, for instance.
Posted: Thu Aug 03, 2006 2:54 pm
by Benjamin
K I'll have to learn more about that.
Posted: Thu Aug 03, 2006 7:51 pm
by Ollie Saunders
feyd wrote:For myself, I use the __autoload() to generate stub classes and a ServiceLocator to actually load and procure real classes. The stubs created by __autoload() log how and where they are used so I can make corrections.
Can you explain that in more depth. With some example code perhaps. What is a stub anyway?
Posted: Thu Aug 03, 2006 10:19 pm
by feyd
ole wrote:Can you explain that in more depth. With some example code perhaps. What is a stub anyway?
I generally consider __autoload() more of an error handling functionality than something magical.
A stub, in this case, is a dynamically created class that fills the requirement for the namespace requested to be filled. My stubs overload a few of the magic methods supported by PHP 5.1. In this way it can handle property and method requests similar to the expected class. It allows PHP to continue execution without breaking fatally because I or a third party forgot to include the proper file or call the ServiceLocator.
I can't show actual code right now as my ServiceLocator is in a massive state of flux, but I'll show you the basics of what my __autoload() does.
Code: Select all
function flash($var)
{
if($var === null)
{
return 'null';
}
elseif(is_array($var))
{
$c = 0;
$o = 'array(';
foreach($var as $k => $v)
{
$o .= ($c > 0 ? ',' : '') . ($k == $c ? '' : flash($k) . '=>') . flash($v);
$c++;
}
$o .= ')';
return $o;
}
elseif(is_scalar($var))
{
return var_export($var,true);
}
elseif(is_object($var))
{
return 'object ' . get_class($var);
}
elseif(is_resource($var))
{
return 'resource ' . get_resource_type($var);
}
else
{
return 'unknown';
}
}
function flashOver($aForetext = null, $aArgs = null)
{
$trace = debug_backtrace();
//var_dump($trace);
if (isset($trace[1]))
{
$line = (isset($trace[1]['line']) ? intval($trace[1]['line']) : '??');
$file = (isset($trace[1]['file']) ? $trace[1]['file'] : 'Unknown file');
$prefix = '(' . $line . ')' . $file . ': ';
}
else
{
$prefix = '';
}
if (func_num_args() == 2 and is_string($aForetext) and is_array($aArgs))
{
$call = $aForetext;
$args = $aArgs;
}
else
{
$call = (!empty($trace[1]['class']) ? $trace[1]['class'] . $trace[1]['type'] : '') . $trace[1]['function'];
$args = $trace[1]['args'];
}
$args = '(' . implode(', ', array_map('flash', $args)) . ')';
return $prefix . $call . $args;
}
if (!function_exists('__autoload'))
{
function __autoload($aClassName)
{
$code =<<<STOP
class {$aClassName}
{
private \$store;
public function __construct()
{
\$this->store = array();
echo flashOver(), PHP_EOL;
}
public function __destruct()
{
echo flashOver(), PHP_EOL;
}
private function __set(\$aVar, \$aValue)
{
echo __CLASS__, (isset(\$this) ? '->' : '::'), \$aVar, ' = ' . flash(\$aValue), PHP_EOL;
\$this->store[\$aVar] = \$aValue;
}
private function __get(\$aVar)
{
echo __CLASS__, (isset(\$this) ? '->' : '::'), \$aVar, PHP_EOL;
return \$this->store[\$aVar];
}
private function __call(\$aFunction, \$aArgs)
{
echo flashOver(__CLASS__ . (isset(\$this) ? '->' : '::') . \$aFunction, \$aArgs), PHP_EOL;
return null;
}
}
STOP;
echo flashOver(), PHP_EOL;
eval($code);
}
}
$a = new UnknownClassName('arg1','arg2');
could possibly kick out:
Code: Select all
(133)/tests/stubTest.php: __autoload('UnknownClassName')
(133)/tests/stubTest.php: UnknownClassName->__construct('arg1', 'arg2')
(??)Unknown file: UnknownClassName::__destruct()
Now, this is just a rough example, but it's a working one.

Posted: Fri Aug 04, 2006 2:13 am
by Benjamin
I guess I have a few more questions related to this so I'll just add to this thread.
If I have a class, which requires subclasses, and I put them all in the same folder, using an autoloader is easy. But what if I don't want all my classes in the same folder. On a large site I could end up with thousands of them. What if I create a class in a folder, and then put all the classes that it requires in a subfolder, with the exception of universal classes such as the database class. Now that makes it more organized, but then how do I use an autoloader with it?
Since the different classes are in different folders, I can't just use 1 autoloader. Can I declare an autoloader as a private function inside of the main class? Or do I just write a little bit of code that automatically includes all the .php files in a certain directory?
Posted: Fri Aug 04, 2006 2:21 am
by jmut
astions wrote:I guess I have a few more questions related to this so I'll just add to this thread.
If I have a class, which requires subclasses, and I put them all in the same folder, using an autoloader is easy. But what if I don't want all my classes in the same folder. On a large site I could end up with thousands of them. What if I create a class in a folder, and then put all the classes that it requires in a subfolder, with the exception of universal classes such as the database class. Now that makes it more organized, but then how do I use an autoloader with it?
Since the different classes are in different folders, I can't just use 1 autoloader. Can I declare an autoloader as a private function inside of the main class? Or do I just write a little bit of code that automatically includes all the .php files in a certain directory?
yes, you can use one autoloader to load all your classes. One way is to use naming convention for your classes though and appropriate dir structure.
For example
library/
...........Controller/
........................Action.php (declares class Controller_Action)
........................Index.php (declares class Controller_Index)
...........Controller.php (declares class Controller)
...etc....
Code: Select all
define('CLASS_PATH',dirname(__FILE__)."/library");
set_include_path(get_include_path() . PATH_SEPARATOR .CLASS_PATH );
function __autoload($classname) {
if (class_exists($classname, false)) {
return;
}
$path = str_replace('_',DIRECTORY_SEPARATOR, $classname);
include_once($path.".php");
}
Edit:
Also take a look at spl_autoload_* family functions
Posted: Fri Aug 04, 2006 7:25 am
by Ollie Saunders
feyd wrote:Now, this is just a rough example, but it's a working one.
Well its very clever, and I understand what its doing but now I don't understand why.
feyd wrote:This gives me documentation of the coupling, but also gives me information of where I could place ServiceLocator calls to load a class giving me a more optimal loading pattern.
It does?
jmut wrote:One way is to use naming convention for your classes though and appropriate dir structure.
Yep that's almost exactly what happens in the ZF.