View Source

<ac:macro ac:name="note"><ac:parameter ac:name="title">Under Construction</ac:parameter><ac:rich-text-body>
<p>Please edit this page and add your ideas about how to &quot;fix&quot; Zend.php. Ideally, the resulting architecture would support extensibility without sacrificing the hallmark simplicity of Zend Framework.</p></ac:rich-text-body></ac:macro>

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{zone-template-instance:ZFDEV:Zend Proposal Zone Template}

{zone-data:component-name}
What to do with Zend.php
{zone-data}

{zone-data:proposer-list}
Zend Framework community
{zone-data}

{zone-data:revision}
0.8 - 26 February 2007: Created from community discussion on fw-general and ZF-958.
0.8.1 - 28 February 2007: Trying to outline various possible solutions
0.8.2 - 6 March 2007: Add option E
{zone-data}

{zone-data:overview}
There has been a long discussion going on about the {{Zend.php}} file, and what should happen with it. Apparently, [TIMTOWTDI|http://acronyms.thefreedictionary.com/TIMTOWTDI]!

Need to distill the following into explicit requirements .. feel free to volunteer :)

Splitting up {{Zend.php}} does not necessarily require creating a bunch of tiny classes.

However, consider proposals like [$19169], where some developers might like to "include" some useful utility functions from their application bootstrap. Ideally, the ZF would include a mechanism and architecture to facilitate this, without just appending more and more functions to {{Zend.php}}

We'll try to put all of the suggestion in line, as it is hard to keep on overview.

*Some example justifications for splitting / cleaning up {{Zend.php}}*

* Why is {{Zend.php}} called {{Zend.php}}? Cleaning up the organization via splitting also helps reduce naming confusion amongst beginners.
* Solves the svn externals issue described in detail previously.
* Why should I load a "debug" function with every request?
* How can I add more debug functions for use when I do want/need it (possibly loaded on-demand, even in my production environment) in a manner consistent with existing debug functions that is "compatible" with other related functions other developers might contribute (i.e. where is the standardization to facilitate community development and enhancement of framework-level "bootstrap" type features?
* Where is a standard mechanism for developers to add "global" functionality to their ZF app (e.g. "plugins" for {{Zend.php}})
* Why is the registry accessed via the oddly named functions in {{Zend.php}}? (Yes, BC, but it would be nice to switch to something cleaner.)
* How should developers extend the functionality mixed into the current {{Zend.php}}? Need to plan for the long-term here ...


----

h2. Solution A:
Move {{Zend.php}} to {{<zf-home>/library/Zend/Zend.php}}. The class name would continue to be {{Zend}}, and no methods within the class will be changed.

The only change in your application would be that you need to change this:

{code}
require_once 'Zend.php';
{code}

To this:

{code}
require_once 'Zend/Zend.php';
{code}

Alternatively, you could add the {{Zend}} directory to your include_path.

*The benefits*
* Zend Framework files contained in a single directory. This simplifies integration of a Zend Framework tree into an application tree. You could use svn:externals, for instance.
* No usage of this class would change. Thus, no API change in the 0.9 release.

*The disadvantages*
* The disadvantage is that it does not follow the ZF convention that class names always match the physical location of the class file. But this is the only class that needs to be an exception to this convention.
* Continued use of "bucket class" does not necessarily address future functionality needs

----

h2. Solution B:
Rename {{Zend.php}} to {{Zend/Core.php}} or {{Zend/Utility.php}} and leave it as a general "core" or "utility" class. Of course, we could mark the original {{library/Zend.php}} as {{@deprecated}, having class Zend temporarily proxy to whatever classes we decide for delegation.

*The benefits*
* Zend Framework files contained in a single directory. This simplifies integration of a Zend Framework tree into an application tree. You could use svn:externals, for instance.

*The disadvantages*
* Continued use of "bucket class" does not necessarily address future functionality needs

----

h2. Solution C:
{code}
static public function loadClass($class, $dirs = null
static public function loadFile($filename, $dirs = null, $once = false)
static public function isReadable($filename)
{code}

Will move to a basic class like {{Zend/Loader.php}}, or a more full-fledged class like {{Zend/FileSystem.php}}, which could also include recursive directory iterators, regular expressions-based file manipulation, etc.

{code}
static public function loadInterface($class, $dirs = null)
static public function exception($class, $message = '', $code = 0)
{code}

Will be deprecated (removed).

{code}
static public function register($index, $newval)
static public function registry($index = null)
static public function isRegistered($index)
static public function initRegistry($registry = 'Zend_Registry')
static public function __unsetRegistry()
{code}

Will be superseded by {{Zend/Registry.php}}.

{code}
static public function dump($var, $label=null, $echo=true)
{code}

Will move to {{Zend/Debug.php}}, which could include a whole host of additional debugging information, interaction with Zend_Log for trace messages, pseudo-breakpoints, etc.

And last but not least:

{code}
static public function compareVersion($version)
{code}

Will either be handled by {{Zend/Environment.php}} or move to something like {{Zend/Version.php}}

*The benefits*
* Zend Framework files contained in a single directory. This simplifies integration of a Zend Framework tree into an application tree. You could use svn:externals, for instance.
* Modular; you don't have to load it if you don't use it
* Consistent; no confusing exceptions or non-standard practices
* Easily add additional functionality at a future date without bloating a core class (additional file loading or debugging methods, for example)
* Eliminates Zend.php class altogether---class is sometimes seen as confusing for new users who think they are including the entire framework or don't understand the purpose of the class

*The disadvantages*
* May break backwards compatibility for applications built before 0.9 (beta). (However, it is better to break compatibility now rather than sometime after 1.0.)
* May need to add one or two additional require_once statements to bootstrap file

----

h2. Solution D:
We still believe having all the core functionality in one file is beneficial. While we want to keep the framework as loosely coupled as possible there are some key APIs which are pretty much used universally throughout the framework. Breaking them each out into separate classes
and files would probably lead to a real performance hit and would require people to require_once() all those components over and over again (which would also be a PITA).

We therefore suggest that these key classes (previously known as {{Zend::}}) all are put in the {{Zend/Core.php}} file.

{code}
library/Zend/Core.php:
----------------------

class Zend_Registry {
static public function register($index, $newval) null);
static public function registry($index = null);
static public function isRegistered($index);
static public function initRegistry($registry = 'Zend_Registry');
static public function __unsetRegistry();
}

class Zend_Loader {
static public function loadClass($class, $dirs = null);
static public function loadFile($filename, $dirs = null, $once =
false);
static public function isReadable($filename);
}

class Zend_Framework {
static public function compareVersion($version)
const VERSION = '0.8.0dev';
}

class Zend_Exception { ... }


// Could live with the following outside of Core.php but it's so small
it really isn't a big deal to keep it for convenience
class Zend_Debug {
static public function dump($var, $label=null, $echo=true)
}
{code}


*The benefits*
* Zend Framework files contained in a single directory. This simplifies integration of a Zend Framework tree into an application tree. You could use svn:externals, for instance.
* Simplifies inclusion into applications, with four fewer require_once statements

*The disadvantages*
* Multiple classes in one file; non-standard practice
* May have to load classes that are not ever used (for example, Zend_Registry)
* Might be premature optimization

----

h2. Solution E:

Very similar to Solution C, but with minor variation on names of classes.

* Methods for class-loading move to {{Zend/Loader.php}}.
* Method {{isReadable()}} also moves to {{Zend/Loader.php}} because it doesn't justify having another class for this one method. If we develop a {{Zend/Filesystem.php}} class in the future, we'll move {{isReadable()}} and deprecate it in {{Zend/Loader.php}}.
* Redesign Registry methods to store a static object in the {{Zend/Registry.php}} class. Allow developer to specify which class to use for this static instance, to allow subclassing. But of course you can also create an instance of Zend_Registry in a traditional manner, with {{new}}.
* Registry also has static methods {{get()}} and {{put()}}, which correspond to the old {{Zend::registry()}} and {{Zend::register()}} methods. Internally, thse methods are simply {{$r = self::getInstance(); return $r\[key\];}} and {{$r = self::getInstance(); $r\[key\] = value;}}.
* Method and const for version moves to {{Zend/Version.php}}.
* Method {{dump()}} moves to class {{Zend_Debug}}.
* Rewrite {{Zend.php}} methods to proxy to new classes, and mark them deprecated in ZF 0.9.0. Remove {{Zend.php}} in the subsequent ZF 1.0.0 RC1.
* Rewrite unit tests.
* Rewrite manual pages.

*The benefits*
* Zend Framework files contained in a single directory. This simplifies integration of a Zend Framework tree into an application tree. You could use svn:externals, for instance.
* Modular; you don't have to load it if you don't use it.
* Consistent; no confusing exceptions or non-standard practices
* Easily add additional functionality at a future date without bloating a core class (additional file loading or debugging methods, for example)
* Eliminates Zend.php class altogether---class is sometimes seen as confusing for new users who think they are including the entire framework or don't understand the purpose of the class
* Only one class is required -- {{Zend/Loader.php}} so the bootstrap will not grow.

*The disadvantages*
* May break backwards compatibility for applications built before 0.9 (beta).
* If additional functions are needed, then need to require_once additional classes.

-------- Original Message --------
Subject: Re: [fw-general] Request for feedback: moving Zend.php to Zend/Zend.php
Date: Mon, 26 Feb 2007 12:41:00 -0800
From: Gavin Vess <gavin@zend.com>
To: fw-general@lists.zend.com

I agree completely with Ralf, Simon, Matt R, and others regarding
eliminating the current Zend.php class and splitting it up to achieve
the flexibility and clarity the ZF has become famous for. In fact, I was
pushing for something similar to this in an uncommitted version
pre-0.7. Bill and I brainstormed about these kinds of changes before
0.7, but it was decided to delay consideration until a later date, in
order to get 0.7 done sooner. I've always strongly disliked the old
registry interface currently found in Zend.php. The creation of the
newer Zend_Registry class was a move towards fixing the API without
breaking BC. If we are going to clean things up in time for ZF 1.0, I
certainly vote to do so before ZF 0.9.

Basically, the idea was to have an ultra-light core for "Zend.php"
(really just a minimalistic framework loader / "bootstrap" - not an
application bootstrap), which would then include optional and
non-optional classes necessary for using the ZF. However, these
"pieces" would be grouped under a subdirectory (e.g. "bootstrap")
instead of mixed in with everything else at the top-level.
{zone-data}

{zone-data:references}
* [$19169] - Example of functionality that has utility, but should remain optional, yet easy to access for those needing it.
{zone-data}

{zone-data:requirements}
h3. @TODO

Most requirements take the form of "foo will do ...." or "foo will not support ...", although different words and sentence structure might be used. Adding functionality to your proposal is requirements creep (bad), unless listed below. Discuss major changes with your team first, and then open a "feature improvement" issue against this component.

{zone-data}

{zone-data:dependencies}
* Zend_Exception
{zone-data}

{zone-data:operation}
The component is instantiated with a mind-link that ...
{zone-data}

{zone-data:milestones}
Describe some intermediate state of this component in terms of design notes, additional material added to this page, and / code. Note any significant dependencies here, such as, "Milestone #3 can not be completed until feature Foo has been added to ZF component XYZ." Milestones will be required for acceptance of future proposals. They are not hard, and many times you will only need to think of the first three below.
* Milestone 1: [design notes will be published here|http://framework.zend.com/wiki/x/sg]
* Milestone 2: Working prototype checked into the incubator supporting use cases #1, #2, ...
* Milestone 3: Working prototype checked into the incubator supporting use cases #3 and #4.
* Milestone 4: Unit tests exist, work, and are checked into SVN.
* Milestone 5: Initial documentation exists.

If a milestone is already done, begin the description with "\[DONE\]", like this:
* Milestone #: \[DONE\] Unit tests ...
{zone-data}

{zone-data:class-list}
* Zend_Magic_Exception
* Zend_Magic (factory class)
* Zend_Magic_MindProbe
* Zend_Magic_MindProbe_Intent
* Zend_Magic_Action
* Zend_Magic_CodeGen
{zone-data}

{zone-data:use-cases}
||UC-01||
{code:php}
require_once 'Zend/Registry.php';
$registry = Zend_Registry::getInstance();
$registry['key'] = 'value';
{code}

||UC-02||
{zone-data}

{zone-data:skeletons}
{composition-setup}
{deck:id=Registry}
{card:label=Registry}
{code:php}
/**
* Extended Zend_Registry class stores a single instance object
* of itself, or a subclass of Zend_Registry.
*/
class Zend_Registry extends ArrayObject
{
/**
* Class name of the singleton registry object.
* @var string
*/
private static $_registryClassName = 'Zend_Registry';

/**
* Registry object provides storage for shared objects.
* @var Zend_Registry
*/
private static $_registry = null;

/**
* Retrieves the default registry instance.
*
* @return Zend_Registry
*/
public static function getInstance()
{
if (self::$_registry === null) {
self::init();
}

return self::$_registry;
}

/**
* Set the default registry instance to a specified instance.
*
* @param Zend_Registry $registry An object instance of type Zend_Registry,
* or a subclass.
* @return void
* @throws Zend_Exception if registry is already initialized.
*/
public static function setInstance(Zend_Registry $registry)
{
if (self::$_registry !== null) {
throw new Zend_Exception('Registry is already initialized');
}

self::$_registry = $registry;
self::setClassName(get_class($registry));
}

/**
* Initialize the default registry instance.
*
* @return void
* @throws Zend_Exception if default registry is already initialized.
*/
protected static function init()
{
self::setInstance(new self::$_registryClassName());
}

/**
* Set the class name to use for the default registry instance.
* Does not affect the currently initialized instance, it only applies
* for the next time you instantiate.
*
* @param string $registryClassName
* @return void
* @throws Zend_Exception if the registry is initialized or if the
* class name is not valid.
*/
public static function setClassName($registryClassName = 'Zend_Registry')
{
if (self::$_registry !== null) {
throw new Zend_Exception('Registry is already initialized');
}

if (!is_string($registryClassName)) {
throw new Zend_Exception("Argument is not a class name");
}

if (!class_exists($registryClassName, false)) {
throw new Zend_Exception("'$registryClassName' class is not found");
}

self::$_registryClassName = $registryClassName;
}

/**
* Unset the default registry instance.
* Primarily used in tearDown() in unit tests.
* @returns void
*/
public static function __unsetRegistry()
{
self::$_registry = null;
}

/**
* getter method, basically same as offsetGet(), except if the key is not
* specified, then the entire registry is returned (iterable).
*
* This method can be called from an object of type Zend_Registry, or it
* can be called statically. In the latter case, it uses the default
* static instance stored in the class.
*
* @param string $index - get the value associated with $index
* @return mixed
* @throws Zend_Exception if no entry is registerd for $index.
*/
public static function get($index)
{
$instance = self::getInstance();

if (!$instance->offsetExists($index)) {
throw new Zend_Exception("No entry is registered for \"$index\"");
}

return $instance->offsetGet($index);
}

/**
* setter method, basically same as offsetSet().
*
* This method can be called from an object of type Zend_Registry, or it
* can be called statically. In the latter case, it uses the default
* static instance stored in the class.
*
* @param string $index The location in the ArrayObject in which to store
* the value.
* @param mixed $value The object to store in the ArrayObject.
* @return void
*/
public static function set($index, $value)
{
$instance = self::getInstance();
$instance->offsetSet($index, $value);
}


/**
* @param string $index
* @returns mixed
*
* Workaround for http://bugs.php.net/bug.php?id=40442 (ZF-960).
*/
public function offsetExists($index)
{
return array_key_exists($index, $this);
}

}
{code}
{card}

{card:label=Version}
{code:php}
/**
* Class to hold the version of this release of Zend Framework.
*/
class Zend_Version
{
const VERSION = '0.9.0dev';

/**
* Compare the specified ZF $version with the current Zend::VERSION of the ZF.
*
* @param string $version A version identifier for the ZF (e.g. "0.7.1")
* @return boolean -1 if the $version is older, 0 if they are the same, and +1 if $version is newer
*
*/
static public function compareVersion($version)
{
return version_compare($version, Zend::VERSION);
}
}
{code}
{card}

{card:label=Loader}
{code:php}
/**
* Class for loading classes and files.
* Holds methods that used to live in Zend.php.
*/
class Zend_Loader
{
public static function loadClass($class, $dirs = null);
public static function loadFile($filename, $dirs = null, $once = false);
public static function isReadable($filename);
}
{code}
{card}

{card:label=Debug}
{code:php}
/**
* Debug class holds old Zend::dump().
*/
class Zend_Debug
{
static public function dump($var, $label=null, $echo=true)
{
// format the label
$label = ($label===null) ? '' : rtrim($label) . ' ';

// var_dump the variable into a buffer and keep the output
ob_start();
var_dump($var);
$output = ob_get_clean();

// neaten the newlines and indents
$output = preg_replace("/\]\=\>\n(\s+)/m", "] => ", $output);
if (PHP_SAPI == 'cli') {
$output = PHP_EOL . $label
. PHP_EOL . $output
. PHP_EOL;
} else {
$output = '<pre>'
. $label
. htmlentities($output, ENT_QUOTES, 'UTF-8')
. '</pre>';
}

if ($echo) {
echo($output);
}
return $output;
}
}
{code}
{card}


{deck}
{zone-data}

{zone-template-instance}]]></ac:plain-text-body></ac:macro>