PathNavigator
[ class tree: PathNavigator ] [ index: PathNavigator ] [ all elements ]

Source for file PathNavigator.php

Documentation is available at PathNavigator.php

  1. <?php
  2.  
  3. /**
  4.  * Facilitates navigation of path strings
  5.  *
  6.  * PHP version 5
  7.  *
  8.  * @category  Text
  9.  * @package   PathNavigator
  10.  * @author    Denny Shimkoski <bytebrite@gmail.com>
  11.  * @copyright 2006-2009 Denny Shimkoski
  12.  * @license   http://www.opensource.org/licenses/mit-license.html MIT
  13.  * @version   SVN: $Id$
  14.  * @link      http://pear.php.net/package/Text_PathNavigator
  15.  */
  16.  
  17. require_once 'PEAR.php';
  18.  
  19. /**
  20.  * Text_PathNavigator
  21.  *
  22.  * @category Text
  23.  * @package  PathNavigator
  24.  * @author   Denny Shimkoski <bytebrite@gmail.com>
  25.  * @license  http://www.opensource.org/licenses/mit-license.html MIT
  26.  * @version  Release: @package_version@
  27.  * @link     http://pear.php.net/package/Text_PathNavigator
  28.  */
  29.  
  30. class Text_PathNavigator implements ArrayAccessCountableIterator
  31. {
  32.     /**
  33.      * Directory separator, i.e, forward or backward slash
  34.      *
  35.      * @var string 
  36.      */
  37.     protected $slash;
  38.     /**
  39.      * Normalized path string
  40.      *
  41.      * @var string 
  42.      */
  43.     protected $path;
  44.     /**
  45.      * Normalized path after explode() on $slash
  46.      *
  47.      * @var array 
  48.      */
  49.     protected $segments = array();
  50.     /**
  51.      * Current path segment index (Iterator implementation)
  52.      *
  53.      * @var int 
  54.      */
  55.     protected $currentIdx = 0;
  56.  
  57.     /**
  58.      * Constructs a new Text_PathNavigator object
  59.      *
  60.      * @param array|string$path  Path string or array of path segments
  61.      * @param string       $slash character used to separate path segments
  62.      *
  63.      * @return string 
  64.      * @access public
  65.      */
  66.     public function __construct($path$slash = DIRECTORY_SEPARATOR)
  67.     {
  68.         $this->slash = $slash;
  69.         $this->path = $this->normalizePath($path);
  70.         if ($this->path !== null{
  71.             $this->segments = explode($this->slash$this->path);
  72.         }
  73.     }
  74.     /**
  75.      * Returns path string
  76.      *
  77.      * @return string 
  78.      * @access public
  79.      */
  80.     public function __toString()
  81.     {
  82.         return implode($this->slash$this->segments);
  83.     }
  84.     /**
  85.      * Removes all leading and trailing slashes from given path string/segment array.
  86.      *
  87.      * @param array|string$path Path string or array of path segments
  88.      *
  89.      * @return string 
  90.      * @access protected
  91.      */
  92.     protected function normalizePath($path)
  93.     {
  94.         if (empty($path)) {
  95.             return null;
  96.         }
  97.         
  98.         if (is_array($path)) {
  99.             $path implode($this->slash$path);
  100.         }
  101.         
  102.         $offset = 0;
  103.         $len strlen($path);
  104.         
  105.         // trim leading slashes
  106.         while ($path{$offset== $this->slash{
  107.             if (++$offset == $len{
  108.                 return null;
  109.             }
  110.         }
  111.         
  112.         // trim trailing slashes
  113.         while ($path{$len - 1== $this->slash{
  114.             --$len;
  115.         }
  116.         
  117.         return substr($path$offset$len $offset);
  118.     }
  119.     /**
  120.      * Slices current path and returns it as a new Text_PathNavigator object
  121.      *
  122.      * @param int $offset If offset is non-negative, the new path will start
  123.                                            at that offset in the path segment array.
  124.                                            If offset is negative, the new path will start
  125.                                            that far from the end of the path segment array.
  126.      * @param int $length If length is given and is positive, the new path
  127.                                           will have that many segments in it. If length is given
  128.                                           and is negative, the new path will stop that many
  129.                                           segments from the end of the path segment array.
  130.                                           If it is omitted, the new path will have everything
  131.                                           from offset up until the end of the path segment array.
  132.      *
  133.      * @return PEAR_Text_PathNavigator 
  134.      * @access public
  135.      */
  136.     public function slice($offset = 0$length = null)
  137.     {
  138.         // PHP gets all confused if you pass a null into the $length param
  139.         // of array_slice(). hence...
  140.         $segments $length
  141.             ? array_slice($this->segments$offset$length)
  142.             : array_slice($this->segments$offset);
  143.             
  144.         $path implode($this->slash$segments);
  145.         
  146.         return new Text_PathNavigator($path$this->slash);
  147.     }
  148.     /**
  149.      * Returns path substring following the given regular expression
  150.      *
  151.      * @param string $pattern regular expression to use for matching
  152.      *
  153.      * @return Text_PathNavigator 
  154.      * @access public
  155.      */
  156.     public function after($pattern)
  157.     {
  158.         $path = null;
  159.         if (preg_match("!$pattern!"$this->path$matchesPREG_OFFSET_CAPTURE)) {
  160.             $path substr($this->path$matches[0][1strlen($matches[0][0]));
  161.         }
  162.         return new Text_PathNavigator($path$this->slash);
  163.     }
  164.     /**
  165.      * Returns path substring preceding the given regular expression
  166.      *
  167.      * @param string $pattern regular expression to use for matching
  168.      *
  169.      * @return Text_PathNavigator 
  170.      * @access public
  171.      */
  172.     public function before($pattern)
  173.     {
  174.         $path = null;
  175.         if (preg_match("!$pattern!"$this->path$matchesPREG_OFFSET_CAPTURE)) {
  176.             $path substr($this->path0$matches[0][1]);
  177.         }
  178.         return new Text_PathNavigator($path$this->slash);
  179.     }
  180.     /**
  181.      * Returns path substring between the given regular expressions $start and $end
  182.      *
  183.      * @param string $start regular expression
  184.      * @param string $end   regular expression
  185.      *
  186.      * @return Text_PathNavigator 
  187.      * @access public
  188.      */
  189.     public function between($start$end)
  190.     {
  191.         return $this->after($start)->before($end);
  192.     }
  193.     /**
  194.      * Returns this path relative to another one. Assumes both paths are absolute.
  195.      *
  196.      * If located in a path $p2, we could navigate to this one
  197.      * using $p2->cd($this->relativeTo($p2))
  198.      *
  199.      * @param mixed $path new path will be expressed in relation to this
  200.      *
  201.      * @return Text_PathNavigator 
  202.      * @access public
  203.      */
  204.     public function relativeTo($path)
  205.     {
  206.         // given path can also be a string or an array of segments,
  207.         // although Text_PathNavigator is preferred
  208.         if (!$path instanceof Text_PathNavigator{
  209.             $path = new Text_PathNavigator($path$this->slash);
  210.         }
  211.         
  212.         // array to hold new path segments
  213.         $_segments = array();
  214.         
  215.         // make a copy of path segments for shift operation
  216.         $segments $this->segments;
  217.         
  218.         // enable shift
  219.         $shift = true;
  220.         
  221.         // for each segment in the given path....
  222.         foreach ($path as $i => $segment{
  223.             // this effectively truncates any "absolute" portion of the two paths,
  224.             // i.e., beginning segments shared in common
  225.             if ($shift && isset($this->segments[$i]&& $this->segments[$i== $segment{
  226.                 array_shift($segments);
  227.             else {
  228.                 // escape from the remaining elements of the given path
  229.                 $_segments['..';
  230.                 // shifting no longer allowed since a differing segment was encountered
  231.                 $shift = false;
  232.             }
  233.         }
  234.         
  235.         // now just tack on any segments left over from the shift operation...
  236.         foreach ($segments as $segment{
  237.             $_segments[$segment;
  238.         }
  239.         
  240.         // ...and return a new Text_PathNavigator object
  241.         return new Text_PathNavigator($_segments$this->slash);
  242.     }
  243.     /**
  244.      * Navigates from this path to another using the specified relative path.
  245.      *
  246.      * @param mixed $path relative path
  247.      *
  248.      * @return Text_PathNavigator 
  249.      * @access public
  250.      */
  251.     public function cd($path)
  252.     {
  253.         // given path can also be a string or an array of segments
  254.         // (probably a string, i.e., '../../some/dir')
  255.         if (!$path instanceof Text_PathNavigator{
  256.             $path = new Text_PathNavigator($path$this->slash);
  257.         }
  258.         
  259.         // make a copy of path segments for pop operation
  260.         $_segments $this->segments;
  261.         
  262.         // for each segment in the given path....
  263.         foreach ($path as $segment{
  264.             // navigate up and down the hierarchy
  265.             if ($segment == '..'{
  266.                 array_pop($_segments);
  267.             else {
  268.                 $_segments[$segment;
  269.             }
  270.         }
  271.         
  272.         // ...and return a new Text_PathNavigator object
  273.         return new Text_PathNavigator($_segments$this->slash);
  274.     }
  275.     /**
  276.      * Maps path segments to variable names given in $template.
  277.      * Suitable for use with PHP's extract() function.
  278.      *
  279.      * @param string $template e.g., '/controller/method/'
  280.      *
  281.      * @return array 
  282.      * @access public
  283.      */
  284.     public function map($template)
  285.     {
  286.         $map explode($this->slash$template);
  287.         foreach ($map as $i => $key{
  288.             $segments[$key= isset($this->segments[$i]$this->segments[$i: null;
  289.         }
  290.         return $segments;
  291.     }
  292.     /**
  293.      * Check if a particular segment exists (ArrayAccess implementation)
  294.      *
  295.      * @param int $i path segment index
  296.      *
  297.      * @return boolean 
  298.      * @access public
  299.      */
  300.     public function offsetExists($i)
  301.     {
  302.         return isset($this->segments[$i]);
  303.     }
  304.     /**
  305.      * Get a particular segment (ArrayAccess implementation)
  306.      *
  307.      * @param int $i path segment index
  308.      *
  309.      * @return string 
  310.      * @access public
  311.      */
  312.     public function offsetGet($i)
  313.     {
  314.         return isset($this->segments[$i]$this->segments[$i: null;
  315.     }
  316.     /**
  317.      * Throws Exception for the purpose of immutability (ArrayAccess implementation)
  318.      *
  319.      * @param int $i path segment index
  320.      *
  321.      * @throws Exception for the purpose of immutability
  322.      * @return void 
  323.      * @access public
  324.      */
  325.     public function offsetUnset($i)
  326.     {
  327.         throw new PEAR_Exception('Text_PathNavigator is immutable');
  328.     }
  329.     /**
  330.      * Throws Exception for the purpose of immutability (ArrayAccess implementation)
  331.      *
  332.      * @param int    $i   path segment index
  333.      * @param string $val path segment
  334.      *
  335.      * @throws Exception for the purpose of immutability
  336.      * @return void 
  337.      * @access public
  338.      */
  339.     public function offsetSet($i$val)
  340.     {
  341.         throw new PEAR_Exception('Text_PathNavigator is immutable');
  342.     }
  343.     /**
  344.      * Returns the number of path segments (Countable implementation)
  345.      *
  346.      * @return int 
  347.      * @access public
  348.      */
  349.     public function count()
  350.     {
  351.         return count($this->segments);
  352.     }
  353.     /**
  354.      * Returns current path segment (Iterator implementation)
  355.      *
  356.      * @return string 
  357.      * @access public
  358.      */
  359.     public function current()
  360.     {
  361.         return $this->segments[$this->currentIdx];
  362.     }
  363.     /**
  364.      * Returns index of current path segment (Iterator implementation)
  365.      *
  366.      * @return int 
  367.      * @access public
  368.      */
  369.     public function key()
  370.     {
  371.         return $this->currentIdx;
  372.     }
  373.     /**
  374.      * Advances to next path segment (Iterator implementation)
  375.      *
  376.      * @return void 
  377.      * @access public
  378.      */
  379.     public function next()
  380.     {
  381.         ++$this->currentIdx;
  382.     }
  383.     /**
  384.      * Rewinds to first path segment (Iterator implementation)
  385.      *
  386.      * @return void 
  387.      * @access public
  388.      */
  389.     public function rewind()
  390.     {
  391.         $this->currentIdx = 0;
  392.     }
  393.     /**
  394.      * Checks if current path segment index is valid (Iterator implementation)
  395.      *
  396.      * @return bool 
  397.      * @access public
  398.      */
  399.     public function valid()
  400.     {
  401.         return isset($this->segments[$this->currentIdx]);
  402.     }
  403. }

Documentation generated on Mon, 14 May 2012 15:00:02 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.