Issues

ZF-958: ZF directory structure is incompatible with use of svn:externals

Description

The recommended directory structure and the recommended use of svn:externals for maintaining user copies of the framework are incompatible. That is, the following structure:


/application
  /models
  /views
  /controllers
/document_root
  /images
  /styles
  .htaccess
  index.php
/library
  /Zend
  Zend.php
  /PEAR
  /SuperLib

cannot be maintained by svn:externals because {{Zend.php}} is sibling to the {{Zend/}} directory. The svn:externals property only works upon directories, not files.

An alternative approach:


/application
  /models
  /views
  /controllers
/document_root
  /images
  /styles
  .htaccess
  index.php
/library
  /ZendFramework
    /Zend
    Zend.php
  /PEAR
  /SuperLib

suffers from the problem of requiring each of the children directories of {{/library}} to be in the include path, resulting in suboptimal performance when working with multiple libraries.

Thanks to ~andries] for [the report!

Comments

One solution I have heard suggested is to move Zend.php under the Zend directory. This would be a single exception to our policy that class hierarchy should match directory hierarchy, but it would solve this issue. For example:


/application
  /models
  /views
  /controllers
/document_root
  /images
  /styles
  .htaccess
  index.php
/library
    /Zend
        Zend.php
        Db.php, etc.
  /PEAR
  /SuperLib

Then users could use svn externals to grab the "library/Zend/" directory and they would get everything they need.

The breakage to backward compatibility impact would be that any usage of this:


require_once "Zend.php";

Would necessarily be rewritten as follows:


require_once "Zend/Zend.php";

But all code invoking the class itself, including static methods and constants, would be unchanged.

Also, the change above could be worked around by adding "library/Zend" to the application's include_path.

Hey Bill,

Yes, this is a possible solution. PEAR does the same thing, and also stores the PEAR.php file within the PEAR root folder itself. Another solution, would be to rename the Zend.php file to Zend_Util, Zend_Base or something similar, although I am not sure if that would be a good idea?

+1 on this idea. I tried to push for something similar with the rework of Zend.php and the registry pre 0.7, but the team voted to push it to the backburner, because the idea included more than just moving Zend.php.

Basically, Zend.php is inflexible and doesn't provide a mechanism for extension. Some good ideas have been proposed as "plugins" or optional "extras", but we don't have the structure in place for hanging these add-ons to Zend.php as bootstrap-level "plugins". Personally, I prefer Andries suggestion as this gets us closer to this mechanism/structure to support loading optional "plugins" like ZF-151.

If we move Zend.php to library/Zend/Bootstrap/Zend.php (or wherever), we could just leave "require_once 'Zend/Bootstrap/Zend.php' as the sole line of code in the original location (/library/Zend.php) for BC without requiring changes to include paths. On the other hand, we could leave something in the original file to emit an error or warning encouraging everyone to update their code to the new location of Zend.php.

Gavin, Bill's also making a good point here. By moving Zend.php to Zend/ all code invoking the class would be unchanged, just for the sake of backwards compatibility.... this is not the case if we rename Zend.php to something different.

Either way, if such change is taking place, I rather see this happen pre 1.0 release.

Code would need to change either way. Here is why:


require 'Zend.php'; // current approach in 0.8 (i.e. works ok now)

If include_path is 'trunk/library/Zend':


require 'Zend.php'; // works
require 'Zend/Session.php'; // does not work

If include_path is 'trunk/library/Zend:trunk/library', then code above works, but we have an extra component in our include path (not a clean solution - i.e. a hack). The hack is not needed, if developers change their code to {{require_once 'Zend/Zend.php'}}. IMHO, changing the include paths is more error prone, risky, and complex "code" to change than changing the code of a ZF app. Also, some situations result in different groups of people responsible for the production environment than the developers who write the code deployed, leading to further complications and headaches when changing external include paths. Thus, some developers might need to add more code to their applications to change the include path on every request :(

If include_path is 'trunk/library' (i.e. what we use now for 0.8), then we don't need a hack in the include path, but we do need the "hack" of having one line of code in 'library/Zend.php' ({{require_once 'Bootstrap/Zend.php'}}), OR developers need to change their code (to ({{require_once 'Bootstrap/Zend.php'}}). The approach I suggested works even if developers make no changes.

I would be in favor of moving {{library/Zend.php}} to below {{library/Zend/}}. This seems to be the only way to support svn:externals using the recommended directory structure without requiring an include_path entry specifically for the Zend Framework. I think there are other reasons to move the functionality of the {{Zend}} class, however, since, as others have pointed out, it fails to meet specific goals, lacks cohesion, and jumbles together completely unrelated functionality - "schizophrenic," I believe, was the term of choice. :)

No matter where the chosen location, it should be noted that either the class must be renamed or the result violates class and file naming conventions. For example, the class in {{library/Zend/Zend.php}} should be renamed {{Zend_Zend}}, or we break the class name for the file:


Zend/Acl.php  --> class Zend_Acl
Zend/Zend.php --> class Zend?

As a side note, PEAR breaks the same conventions:


PEAR/PEAR.php --> class PEAR
PEAR/Log.php  --> class PEAR_Log

Of course, we could mark the original {{library/Zend.php}} as {{@deprecated}}, having class {{Zend}} temporarily proxy to whatever classes we decide for delegation (e.g., {{Zend_Util}}, {{Zend_Loader}}, {{Zend_Registry}}, {{Zend_Debug}}). I believe that this would be similar to, if not the same as, Gavin's suggestion.

I would like to have addressed this before 0.9.0, since we will be more concerned about backward compatibility thereafter, since we are striving for this to be a "beta" (API stable) release.

ok, so a roundup:


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

Will move to Zend/Loader.php


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

Will be deprecated (removed)


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()

Will move to Zend/Registry.php


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

Will move to Zend/Debug.php

And last but not least:


static public function compareVersion($version)

Will be supported by Zend_Environment as follows (Simon Mundy):


$environment = new Zend_Environment(new Zend_Environment_Core('framework'));
if ($environment->framework->isVersion('0.9')) {
    // ...
}

In the 0.9 release Zend.php should temporarily proxy these methods, and mark the file as deprecated. A note should be added to encourage developers to update their code according to these changes.

Question is if we all agree on this? :)

Not to forget. All unit tests should be updated, according to these changes.

Zend_Environment is not intended for use during ordinary execution on production applications with every request. Zend_Environment's goals focus on robustness and functionality, not performance. Thus, Zend_Environment will not be (and should not be) loaded with every request, and {{compareVersion}} must remain in a "light" loader for the ZF.

The outline that Andries gives above sounds pretty good, but after seeing the level of diverse discussion on the email thread, it seems clear that we should write a proposal for this change, and discuss more use cases.

I would add to Andries' outline that we need a class Zend_Version.

Rewrite summary.

Resolved. Zend.php file is deprecated, and no longer used by any class in Zend Framework. Therefore checking it out from svn is optional.