Yii Framework Forum: YiiBase::import() support for multiple classes in one file - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

YiiBase::import() support for multiple classes in one file Rate Topic: -----

#1 User is offline   ShabbyRobe 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 42
  • Joined: 13-October 08

Posted 16 October 2008 - 11:47 PM

It would be very useful to be able to package many classes into a single file and still use Yii's import declarations. This would also be in keeping with Yii's ability to dramatically increase performance using APC and the stated purpose of yiilite.

I have been hacking on it and have a potential solution, if you think it would be beneficial. I'll use the tired example of the class nobody has ever really built: Animal!

external class file (apppath/something/Animal.php)

<?php
class Animal {
//snip
}
class Duck extends Animal {
//snip
}
class MutantDuck extends Duck implements Mutant {
//snip
}
?>


controller:

<?php
class AwesomeController extends CController
{
    function actionDoWonderfulThings()
    {
        if (isset($_REQUEST['inHell']))
            $ohDear = new MutantDuck();
        else
            $sweet = new WonderfulThing();
    }
}
?>


If I put the appropriate import declarations at the top of the controller, I currently have to specify the force include parameter:

<?php Yii::import("application.something.Animal", true); ?>

in order to expect that the MutantDuck will be available in the event that $_REQUEST['inHell'] is set. That means I'm loading a whole bunch of classes I might not necessarily need - if we're not in hell, I can't imagine there would be too many mutant ducks! ;)

The idea I had to solve the problem is to use a different character to distinguish between a path (which we're using '.' for), and a class name which is the child of a file (for which i thought we could use '/').

The following specifies that the class MutantDuck, present in the file Animal, should be imported:

<?php Yii::import("application.something.Animal/MutantDuck"); ?>


The disadvantage is that we need to have an import declaration for each child class we want to be autoloadable.. for example, if the controller needed to instantiate a regular Duck under certain circumstances, we would need to import both:

<?php
Yii::import("application.something.Animal/Duck");
Yii::import("application.something.Animal/MutantDuck");
?>


The following changes can be made to YiiBase::import() and YiiBase::getPathOfAlias() to support it (but i'm sure there will be a better way to do it). The changes are not huge, but I have not thoroughly tested them yet:

<?php
public static function import($alias,$forceInclude=false)
{
if(isset(self::$_imports[$alias]))  // previously imported
return self::$_imports[$alias];

$classPos=strrpos($alias,'.');
$childPos=strrpos($alias,'/');

if(isset(self::$_coreClasses[$alias]) || ($classPos===false && $childPos===false))  // a simple class name
{
self::$_imports[$alias]=$alias;
if($forceInclude && !class_exists($alias,false))
{
if(isset(self::$_coreClasses[$alias])) // a core class
require_once(YII_PATH.self::$_coreClasses[$alias]);
else
require_once($alias.'.php');
}
return $alias;
}

if(($className=(string)substr($alias,max($classPos,$childPos)+1))!=='*' && class_exists($className,false))
return self::$_imports[$alias]=$className;

if(($path=self::getPathOfAlias($alias))!==false)
{
if($className!=='*')
{
self::$_imports[$alias]=$className;
if($forceInclude)
require_once($path.'.php');
else
self::$_classes[$className]=$path.'.php';
return $className;
}
else  // a directory
{
set_include_path(get_include_path().PATH_SEPARATOR.$path);
return self::$_imports[$alias]=$path;
}
}
else
throw new CException(Yii::t('yii#Alias "{alias}" is invalid. Make sure it points to an existing directory or file.',
array('{alias}'=>$alias)));
}

public static function getPathOfAlias($alias)
{
if(isset(self::$_aliases[$alias]))
return self::$_aliases[$alias];
else if(($pos=strpos($alias,'.'))!==false)
{
$pathAlias=$alias;
// remove the child class portion of the alias
if(($childPos=strrpos($alias,"/"))!==false)
$pathAlias = substr($alias,0,$childPos);

$rootAlias=substr($alias,0,$pos);
if(isset(self::$_aliases[$rootAlias]))
return self::$_aliases[$alias]=rtrim(self::$_aliases[$rootAlias].DIRECTORY_SEPARATOR.str_replace('.',DIRECTORY_SEPARATOR,substr($pathAlias,$pos+1)),'*'.DIRECTORY_SEPARATOR);
}
return self::$_aliases[$alias]=false;
}
?>

1

#2 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,856
  • Joined: 04-October 08
  • Location:DC, USA

Posted 17 October 2008 - 06:07 AM

Thank you for the explorational investigation.

My opinion is that we should keep things simple. The current rule is that each public class should occupy a separate class file while private classes may stay inside a public class file. This is a widely accepted convention (e.g. Java, Flex). It also makes code maintenance easier.

I believe your approach will increase performance when using APC. However, the result won't be as dramastic as yiilite because you don't want to pack tens of classes into a single file (like yiilite) for your project. And if so, you should just include the merged file so that APC can take care of it.


0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users