Issues

ZF-151: Zend::includePath(), excludePath(), correctPath()

Description


/**
 * Zend::includePath()
 *
 * Adds new path to PHP's environment 'include_path'.
 *
 * Valid examples of $includePath are:
 * $includePath = array('new/path', 'another/new/path');
 * $includePath = 'new/path' . PATH_SEPARATOR . 'another/new/path';
 * $includePath = 'new/path';
 *
 * @param   string|array $includePath Path which must be added to
 *          'include_path' enveronment. Array, string or string separated
 *          with PATH_SEPARATOR.
 * @return  boolean TRUE if include_path was changed, else FALSE.
 */
static public function includePath($includePath)
{
    // Exploding current 'include_path'
    $curr_path  = explode(PATH_SEPARATOR, ini_get('include_path'));
    // If $includePath is string, then trying to explode it
    if (!is_array($includePath)) {
        $includePath = explode(PATH_SEPARATOR, $includePath);
    }
    // Merging current 'include_path' and new include paths and leaving only
    // unique paths.
    $new_path = array_unique(array_merge($curr_path, (array)$includePath));
    // If new and old include paths differs then setting new 'include_path'
    // and return true
    if (0 !== count(array_diff($new_path, $curr_path))) {
        ini_set('include_path', implode(PATH_SEPARATOR, $new_path));
        return true;
    }
    // Else return false
    return false;
}


/**
 * Zend::excludePath()
 *
 * This method is similar to (@link Zend::includePath()), except this
 * removes given in param path from PHP's environment 'include_path'.
 *
 * Valid examples of $excludePath are:
 * $excludePath = array('new/path', 'another/new/path');
 * $excludePath = 'new/path' . PATH_SEPARATOR . 'another/new/path';
 * $excludePath = 'new/path';
 *
 * @param   string|array $excludePath Path which must be removed from
 *          'include_path' enveronment. Array, string or string separated
 *          with PATH_SEPARATOR.
 * @return  boolean TRUE if include_path was changed, else FALSE.
 */
static public function excludePath($excludePath)
{
    // Exploding current 'include_path'
    $curr_path  = explode(PATH_SEPARATOR, ini_get('include_path'));
    // If $excludePath is string, then trying to explode it
    if (!is_array($excludePath)) {
        $excludePath = explode(PATH_SEPARATOR, $excludePath);
    }
    // Removing $excludePath paths from 'include_path' enveronment
    $new_path = array_diff($curr_path, (array)$excludePath);
    // If new and old include paths differs then setting new 'include_path'
    // and return true
    if (0 !== count(array_diff($new_path, $curr_path))) {
        ini_set('include_path', implode(PATH_SEPARATOR, $new_path));
        return true;
    }
    // Else return false
    return false;
}

Also I've added :

```

in the begining of Zend.php, so now even if Zend is not in include path then it will be in it :)

Comments

Also today I've added anothe method to Zend to araise usability :)


/**
 * Zend::includeIncubator()
 *
 * Dummy method which allows you to cosily include Zend's incubator.
 *
 * @param   string  $pathToIncubator (optional) Path to Zend's incubator. By
 *          default is '../incubator'.
 * @param   boolean $pathIsRelative  (optional) Perform path as realtive to
 *          Zend's core dir if TRUE or absolute if FALSE.
 * @throws  Zend_Exception If given path is not available or incorect.
 * @return  string Performed path to Zend's incubator.
 */
static public function includeIncubator($pathToIncubator = null, $pathIsRelative = true)
{
    // If param $pathToIncubator wasn't set, or was set to null then set
    // $pathToIncubator equal to '../incubator' and $pathIsRelative to true.
    if (null === $pathToIncubator) {
       $pathToIncubator = '..' . DIRECTORY_SEPARATOR . 'incubator';
    }
    // Perform relative path
    if ($pathIsRelative) {
        $pathToIncubator = realpath(dirname(__FILE__)
                         . DIRECTORY_SEPARATOR
                         . ltrim($pathToIncubator, '\\/'));
    }
    $pathToIncubator = rtrim($pathToIncubator, '\\/');
    // If path is not valid throws Zend_Exception.
    if (!is_dir($pathToIncubator)) {
        $error = sprintf("Cannot find '%s' path. Seems like this directory "
                         . "is not available or path is incorrect.", $pathToIncubator);
        throw new Zend_Exception($error);
    }
    // Else append Zend's incubator path to 'include_path' environment of PHP
    self::includePath($pathToIncubator);
    // And finally return performed path (for debug purposes)
    return $pathToIncubator;
}

So now I can use it like this:


<?php
require_once 'library/zend.php';
Zend::includeIncubator();
?>

PS Sorry for chain-post, but I ddn't found how to edit my first comment.

Nice addition IMHO so that we don't have to use .htaccess or ini_set but just include "/somewhere/in/the/filesytem/lib/Zend.php" and makes deplyoment easier

I've rewrote those methods (includePath and excludePath) and removed includeIncubator() because it's not necessary anyway... so now I have following lines in my Zend.php [code] <?php // ... skipped ... /** * Include Zend's core dir to 'include_path' configuration option. */ Zend::includePath(realpath(dirname(FILE))); // ... skipped ... final class Zend { // ... skipped ... /** * Zend::includePath() * * Add new path to PHP's 'include_path' configuration option. * * Valid examples of $includePath are: * $includePath = array('new/path', 'another/new/path'); * $includePath = 'new/path' . PATH_SEPARATOR . 'another/new/path'; * $includePath = 'new/path'; * * Note that second param $isRelative must be true only for paths that are * relative to Zend core path (library). * * @see Zend::correctPath() * @param string|array $includePath Array or a string with path that you * want to add to 'include_path' configuration option. * @param boolean $isRelative (optional) If TRUE, then given path will be * procesed as path relative to Zend's library path. * @throws Zend_Exception If given path(s) is not available or incorect. * @return boolean TRUE if 'include_path' was changed. FALSE if given * path(s) were already set in 'include_path' */ static public function includePath($includePath, $isRelative = false) { // Getting current include_path configuration option $curr_path = explode(PATH_SEPARATOR, get_include_path()); if (!is_array($includePath)) { $includePath = explode(PATH_SEPARATOR, $includePath); } // Correcting paths according to system's standarts (see Zend::correctPath()) self::correctPath($curr_path); self::correctPath($includePath); $error = false; // Working with paths in array foreach ($includePath as $id => $path) { // Process relative path if ($isRelative) { $path = realpath(dirname(FILE) . DIRECTORY_SEPARATOR . ltrim($path, '\/')); } // Check if path exist if (!is_dir($path)) { $error = sprintf("Cannot find '%s' path. Seems like this path " . "is not available or path is incorrect. " . "Please note that path(s) cannot contain " . "PATH_SEPARATOR char when \$includePath is " . "array.", $includePath[$id]); } $includePath[$id] = $path; } /** * @todo throwing exceptions inside foreach could cause leaks, use a workaround * like this until a fix is available * * @link http://bugs.php.net/bug.php?id=34065 */ if ($error) { throw new Zend_Exception($error); } // Merging current 'include_path' and new include paths and leaving only // unique paths. $new_path = array_unique(array_merge($curr_path, $includePath)); // If new and old include paths differs then setting new 'include_path' // and return true if (0 !== count(array_diff($new_path, $curr_path))) { set_include_path(implode(PATH_SEPARATOR, $new_path)); return true; } // Else return false return false; }

/**
 * Zend::excludePath()
 *
 * This method is pretty similar to (@link Zend::includePath()), except this
 * removes given in param path from PHP's 'include_path' configuration option.
 *
 * Valid examples of $excludePath are:
 * $excludePath = array('new/path', 'another/new/path');
 * $excludePath = 'new/path' . PATH_SEPARATOR . 'another/new/path';
 * $excludePath = 'new/path';
 *
 * @see     Zend::correctPath()
 * @param   string|array $includePath Array or a string with path that you
 *          want to remove from 'include_path' configuration option.
 * @return  boolean TRUE if 'include_path' was changed. FALSE if given
 *          path(s) were not found in 'include_path'
 */
static public function excludePath($excludePath)
{
    // Getting current include_path configuration option
    $curr_path  = explode(PATH_SEPARATOR, ini_get('include_path'));
    if (!is_array($excludePath)) {
        $excludePath = explode(PATH_SEPARATOR, $excludePath);
    }
    // Correcting paths according to system's standarts (see Zend::correctPath())
    self::correctPath($curr_path);
    self::correctPath($excludePath);
    // Creating new path with all previous paths except those given in $excludePath
    $new_path = array_diff($curr_path, $excludePath);
    // If new and old include paths differs then setting new 'include_path'
    // and return true
    if (count($new_path) != count($curr_path)) {
        ini_set('include_path', implode(PATH_SEPARATOR, $new_path));
        return true;
    }
    // Else return false
    return false;
}


/**
 * Zend::correctPath()
 *
 * Dummy method to correct given path according to system's specified
 * directory separator. If given $pathToCorrect is an array, then all
 * values in this array will be corrected, and array will be rturned.
 *
 * @uses    Zend::includePath()
 * @param   string|array $pathToCorrect String or array with path(s) to correct
 * @return  string|array Corrected path(s)
 */
static public function correctPath(&$pathToCorrect)
{
    foreach((array)$pathToCorrect as $key => $value) {
        $pathToCorrect[$key] = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $value);
    }
    return $pathToCorrect;
}

} [/code]

So now in index.php of my project I have such lines:

[code] /** * Setting include_path configuration option */ Zend::includePath(array('../incubator/', '../draft/'), true); [/code]

Let us coordinate with Art Hundiak who is working on Zend_Registry in the incubator. Then we can rollout a tested version of Zend.php with various enhancements at the same time, instead of multiple times.

I can't find how to edit comments so that's why I've decided to edit main post...

I found that it's easier to read the code here, not in the main post. Sorry for chain-posts:

The latest idea which I currently use in Zend.php is:


    /**
     * Zend::includePath()
     *
     * Add new path to PHP's 'include_path' configuration option.
     *
     * Valid examples of $includePath are:
     * $includePath = array('new/path', 'another/new/path');
     * $includePath = 'new/path' . PATH_SEPARATOR . 'another/new/path';
     * $includePath = 'new/path';
     *
     * Note that second param $isRelative must be true only for paths that are
     * relative to Zend core path (library).
     *
     * @see     Zend::correctPath()
     * @param   string|array $includePath Array or a string with path that you
     *          want to add to 'include_path' configuration option.
     * @param   boolean $isRelative (optional) If TRUE, then given path will be
     *          procesed as path relative to Zend's library path.
     * @throws  Zend_Exception If given path(s) is not available or incorect.
     * @return  boolean TRUE if 'include_path' was changed. FALSE if given
     *          path(s) were already set in 'include_path'
     */
    static public function includePath($includePath, $isRelative = false)
    {
        // Getting current include_path configuration option
        $curr_path  = explode(PATH_SEPARATOR, get_include_path());
        if (!is_array($includePath)) {
            $includePath = explode(PATH_SEPARATOR, $includePath);
        }
        // Correcting paths according to system's standarts (see Zend::correctPath())
        self::correctPath($curr_path);
        self::correctPath($includePath);
        $error = false;
        // Working with paths in array
        foreach ($includePath as $id => $path) {
            // Process relative path
            if ($isRelative) {
                $path = realpath(dirname(__FILE__)
                      . DIRECTORY_SEPARATOR
                      . ltrim($path, '\\/'));
            }
            // Check if path exist
            if (!is_dir($path)) {
                $error = sprintf("Cannot find '%s' path. Seems like this path "
                                 . "is not available or path is incorrect. "
                                 . "Please note that path(s) cannot contain "
                                 . "PATH_SEPARATOR char when \$includePath is "
                                 . "array.", $includePath[$id]);
            }
            $includePath[$id] = $path;
        }
        /**
         * @todo throwing exceptions inside foreach could cause leaks, use a workaround
         *       like this until a fix is available
         *
         * @link http://bugs.php.net/bug.php?id=34065
         */
        if ($error) {
            throw new Zend_Exception($error);
        }
        // Merging current 'include_path' and new include paths and leaving only
        // unique paths.
        $new_path = array_unique(array_merge($curr_path, $includePath));
        // If new and old include paths differs then setting new 'include_path'
        // and return true
        if (0 !== count(array_diff($new_path, $curr_path))) {
            set_include_path(implode(PATH_SEPARATOR, $new_path));
            return true;
        }
        // Else return false
        return false;
    }
    
    
    /**
     * Zend::excludePath()
     *
     * This method is pretty similar to (@link Zend::includePath()), except this
     * removes given in param path from PHP's 'include_path' configuration option.
     *
     * Valid examples of $excludePath are:
     * $excludePath = array('new/path', 'another/new/path');
     * $excludePath = 'new/path' . PATH_SEPARATOR . 'another/new/path';
     * $excludePath = 'new/path';
     *
     * @see     Zend::correctPath()
     * @param   string|array $includePath Array or a string with path that you
     *          want to remove from 'include_path' configuration option.
     * @return  boolean TRUE if 'include_path' was changed. FALSE if given
     *          path(s) were not found in 'include_path'
     */
    static public function excludePath($excludePath)
    {
        // Getting current include_path configuration option
        $curr_path  = explode(PATH_SEPARATOR, ini_get('include_path'));
        if (!is_array($excludePath)) {
            $excludePath = explode(PATH_SEPARATOR, $excludePath);
        }
        // Correcting paths according to system's standarts (see Zend::correctPath())
        self::correctPath($curr_path);
        self::correctPath($excludePath);
        // Creating new path with all previous paths except those given in $excludePath
        $new_path = array_diff($curr_path, $excludePath);
        // If new and old include paths differs then setting new 'include_path'
        // and return true
        if (count($new_path) != count($curr_path)) {
            ini_set('include_path', implode(PATH_SEPARATOR, $new_path));
            return true;
        }
        // Else return false
        return false;
    }
    
    
    /**
     * Zend::correctPath()
     *
     * Dummy method to correct given path according to system's specified
     * directory separator. If given $pathToCorrect is an array, then all
     * values in this array will be corrected, and array will be rturned.
     *
     * @uses    Zend::includePath()
     * @param   string|array $pathToCorrect String or array with path(s) to correct
     * @return  string|array Corrected path(s)
     */
    static public function correctPath(&$pathToCorrect)
    {
        foreach((array)$pathToCorrect as $key => $value) {
            $pathToCorrect[$key] = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $value);
        }
        return $pathToCorrect;
    }

So in my project's index.php I have such string, for example:


// include_path is
// '.:/home/ixti/includes'
require_once 'lib/Zend.php';
Zend::includePath(array('/', '../incubator'), true);
// now include_path is
// '.:/home/ixti/includes:/home/ixti/includes/lib:/home/ixti/includes/incubator'

Gavin Vess, I don't clearly understood what you are going to do, but if my help or assistance is needed then I'll be glad to assist :)

Anyhow if my code is usefull and you'll include it into Zend, I'll be pleased. :)

Changing fix version to 0.6.0.

Various changes have been made to related functions in Zend.php over the last few months. I continue to see value in your functions above, but some rework would be needed. Based on current guidelines from the team, I believe the scope of changes above require a proposal. Recently the ZF team has clearly stated that we need to avoid adding features to the ZF (before ZF 1.0) without properly following the process for proposing new features (i.e. proposals).

In order to proceed, I suggest you submit this as a proposal. The greater visibility of a proposal may also help attract input and contributions from the community. I believe the ideas and functions above have a greater chance of acceptance and adoption by the community, if they are contributed in the scope of a larger effort (a proposal addresses path manipulation utility functions for the ZF).

If you submit a proposal for these functions, please provide several use cases showing the community why and how these functions can be used with the current Zend.php related functions, autoloading, the ZF incubator, etc. These use cases attract community support and suggestions for enhancements :)

Ok. I'll write a proposal as soon as possible.