Location: PHPKode > projects > ZZ/OSS Installer > zic-1.1.0dev1/installer/lib/ZZOSS_Package/Dependency.php
<?php   
    /*
    Copyright (C) 2001-2004 ZZOSS GbR, http://www.zzoss.com

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    */
    
    /**
    @version $Id: Dependency.php,v 1.6 2004/04/02 12:34:34 ordnas Exp $
    @copyright Copyright &copy; 2001-2004 ZZ/OSS GbR, http://www.zzoss.com
    @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
    */
	
    require_once 'Package.php';
    
class ZZOSS_PackageDependency
{
    var $queue;
    
    var $step;
    
    var $process;
    
    var $result;
    
    var $deps = array();
    
    var $packages_check = array();
    
    var $package_name;
    
    var $package;
    
    var $dep;
    
    var $error = false;
    
    var $pending = array();
    
    /**
    * Stack of those packages that depend on another package.
    */
    var $_circular_pkgs = array();
    
    /**
    * Stack of those packages that are needeb by another package.
    */
    var $_circular_deps = array();
    
    /**
    * Packages not defined in application.xml.
    */
    var $undefined = array();
    
    function run($packages, $packages_resolve, $packages_versions, $actions)
    {
        $this->packages = $packages;
        $this->packages_resolve = $packages_resolve;
        $this->packages_versions = $packages_versions;
        $this->actions = $actions;
        
        // currently only install dependencies are checked
        // in future an enhanced algorithm will check for proper dependencies
        $this->_composePackageArray('I');
        
        // catch errors
        $this->_checkDependencies();
        
        // HACK: todo: extend dependency function for uninstall / update deps
        $this->step--;
        if($process_queue_array = $this->_composeQueue('R')){
            $this->queue[$this->step] = $process_queue_array;
            $this->step++;
        }
        if($process_queue_array = $this->_composeQueue('U')){
            $this->queue[$this->step] = $process_queue_array;
            $this->step++;
        }
        
        return $this->queue;
    }
    
    function hasPendingDeps()
	{
		return count($this->pending);
	}
    
    function getDeps()
    {
        return $this->result;
    }
    
    function hasUndefinedPackages()
    {
        return count($this->undefined);
    }
    
    function getUndefinedPackages()
    {
        return $this->undefined;
    }
    
    function getPending()
    {
        return $this->pending;
    }
    
    function getError()
    {
        return $this->error;
    }
    
    function isError()
    {
        return strlen($this->error);
    }
    
    function getCheckedNames()
    {
        return $this->packages_check_names;
    }
    
	function _composeQueue($char) {
		$result = false;
		if(is_array($this->actions)) {
			foreach($this->actions as $key => $val) {
				if($val == $char) {
					$name = $this->packages_resolve[$key];
					$result[$name] = $char;
				}
			}
		}
		return $result;
	}
	
	function _composePackageArray($value)
	{
		$result = array();
		if(is_array($this->actions)) {
			foreach($this->actions as $key=>$val) {
				if($val == $value) {
					$name = $this->packages_resolve[$key];
					$this->packages_check[$name] = $this->packages[$name];
                    $this->packages_check_names[$this->packages[$name]['name']] = true;
				}
			}
		}
	}
    
    function _checkDependencies()
	{
		$result = array();
		if(is_array($this->packages_check)) {
			$progress = true;
			$this->step = 0;
			$packages_new = $this->packages;
			while($progress) {
				$progress = false;
				$this->packages = $packages_new;
                //echo $this->step.'<hr/>';
				foreach($this->packages_check as $key => $val) {
                    $this->package_name = $key;
                    $this->package = $val;
					if(!isset($val["_solved"]) || !$val["_solved"]) {
						if($this->_checkDependency()){
  							// TODO: extend function for uninstall / update dependencies
                            //echo 'Resolved: '.$this->step.': '.$key.'<br/>';
							$this->queue[$this->step][$key] = 'I';
                            $progress = true;
							$packages_new[$key]["installed"] = $val["release"]["version"];
							$this->packages_check[$key]["_solved"] = true;
						} else {
                            /*
                            if(!isset($this->queue[$this->step][$key])){
                                $this->queue[$this->step+1][$key] = 'I';
                            }
                            */
                            //echo 'Not resolved: '.$this->step.': '.$key.'<br/>';
                        }
					}
				}
				$this->step++;
			}
            /*
            echo '<pre>';
            print_r($packages_new);
            echo '</pre>';
            */
		}
	}
    
	function _checkDependency()
	{
        $resolve = true;
        /*
        echo '<pre>';
        print_r($this->package);
        echo '<pre>';
        */
        if(!isset($this->package["release"]["deps"])){
            return true;
        }
        
		$deps = $this->package["release"]["deps"]["dep"];
        
		if(!is_array($deps) || !count($deps)) {
            return true;
        } else {
            //echo '<h1>'.$this->package['name'].'</h1>';
            //echo '<pre>';
            //print_r($deps);
            //echo '</pre>';
			foreach($deps as $dep) {
				$this->dep = ZZOSS_InstallerUtils::trimArray($dep);    
				// check for package dependency
                
				if(!$this->_checkDependencyType()){
                    $resolve = false;
                }
			}
		}
        //var_dump($resolve); echo '<br/>';
		return $resolve;
	}

    /**
    * @todo Rewrite to use dep plugins, just like other plugins
    * @todo Routines for all PEAR deps
    */
    function _checkDependencyType()
    {
        $dep_result = true;
        //$this->deps = array();
        switch($this->dep['@']['type']){
            case 'pkg':
                // If the dpendency is optional, don't bother about it
                if(isset($this->dep['@']['optional']) && $this->dep['@']['optional'] == 'yes'){
                    return true;
                }
                
                // Check if the needed package is defined in application.xml
                if(!isset($this->packages_versions[$this->dep["#"]])){
                    //echo $this->dep["#"].'<- '.$this->package_name.'<br>';
                    $pkg_undef_name = $this->dep["#"];
                    if(isset($this->dep["@"]["version"]) && strlen($this->dep["@"]["version"])){
                        $pkg_undef_name .= '-'.$this->dep["@"]["version"];
                        $this->undefined[$pkg_undef_name]['version'] = $this->dep["@"]["version"];
                    }
                    $this->undefined[$pkg_undef_name]['name'] = $this->dep["#"];
                    $this->undefined[$pkg_undef_name]['needed_by'][] = array($this->package_name => $this->dep["@"]["rel"]);
                    //print_r($this->undefined[$pkg_undef_name]['needed_by']);
                    //$this->undefined[$pkg_undef_name]['needed_by'] = array_unique($this->undefined[$pkg_undef_name]['needed_by']);
                    //print_r($this->undefined[$pkg_undef_name]['needed_by']);
                    $this->error = 'Some packages not defined by application';
                } else {
                    
                    $versions = $this->packages_versions[$this->dep["#"]];
                    $dep_result = false;
                    $pending = array();
                    foreach($versions as $v_available => $val){
                        if(!isset($this->dep["@"]["version"])){
                            $this->dep["@"]["version"] = null;
                        }
                        if(!$this->_isResolved($this->dep['@']['rel'], $this->dep["#"], $v_available, $this->dep["@"]["version"])){
                            $pending[$this->dep["#"].'-'.$v_available]['needed_by'][] = array($this->package_name => $this->dep["@"]["rel"]);
                            //$pending[$this->dep["#"].'-'.$v_available]['needed_by'] = array_unique($pending[$this->dep["#"].'-'.$v_available]['needed_by']);
                        } else {
                            $dep_result = true;
                            unset($this->pending[$this->dep['#'].'-'.$v_available]);
                            //echo 'UNSET PENDING: '.$this->dep['#'].'<br/>';
                        }
                    }
                    if(!$dep_result){
                        //echo 'SET PENDING: '.$this->dep['#'].'<br/>';
                        $this->pending = $pending + $this->pending;
                    }
                }
                break;
        }
        
        return $dep_result;
    }
    
    function _isResolved($dep_rel, $dep_name, $v_available, /*$is_installed,*/ $v_required = NULL)
    {
        $is_available = false;
        // If no version has been defined, we check, if the available version
        // is in the list of needed packages
        if(isset($this->packages_check_names[$dep_name])){
             $is_available = true;
        }
        
        $is_installed = isset($this->packages[$this->dep["#"].'-'.$v_available]["installed"]);
        
        $is_resolved = true;
        
        switch($dep_rel) {
            case 'ge':
                // greater-or-equal comparing
                if($is_available && $is_installed){
                    //echo "ge - $dep_name: $v_available >= $v_required<br/>";
                    $is_resolved = version_compare($v_available, $v_required, $dep_rel);
                } else {
                    //var_dump($is_installed);
                    //echo "ge - $dep_name: !\$is_available<br/>";
                    $is_resolved = false;
                }
                break;
            case 'le':
                // lower-or-equal comparing
                // greater-or-equal comparing
                if($is_available && $is_installed){
                    $is_resolved = version_compare($v_available, $v_required, $dep_rel);
                } else {
                    $is_resolved = false;
                }
                break;
            case 'has':
                //echo "has - $dep_name: $is_installed<br/>";
                $is_resolved = $is_installed;
                //$is_resolved = $is_available;
                break;
            default:
                $dep_rel = 'eq';
                // equal comparing
                // greater-or-equal comparing
                if($is_available && $is_installed){
                    $is_resolved = version_compare($v_available, $v_required, $dep_rel);
                } else {
                    $is_resolved = false;
                }
                break;
        }
        
        // check for circular dependencies:
        // if the needed package has a dependency on a package that depends on it
        if(
            isset($this->_circular_pkgs[$this->dep["#"]]) &&
            in_array($this->package['name'], $this->_circular_pkgs[$this->dep["#"]]) &&
            isset($this->_circular_deps[$this->package['name']]) &&
            in_array($this->dep["#"], $this->_circular_deps[$this->package['name']])){
            /*
            echo "Circular dependency between '".$this->dep["#"]."' and '".$this->package['name']."'.";
            //$this->_unsetCircular();
            echo '<pre>deps:';
            print_r($this->_circular_deps);
            echo 'pkgs:';
            print_r($this->_circular_pkgs);
            echo '</pre>';
            */
            return PEAR::raiseError("Circular dependency between '".$this->dep["#"]."' and '".$this->package['name']."'.");
        }
        
        if(!$is_resolved) {
           $this->_setCircularDeps();
        } else {
            $this->_unsetCircular();
        }
        
        return $is_resolved;
    }

    function _setCircularDeps()
    {
        if(!isset($this->_circular_deps[$this->dep["#"]]) && !isset($this->_circular_pkgs[$this->package['name']])) {
            /*
            echo '<br/>';
            echo "Setting deps: '".$this->dep["#"]."'.";
            echo '<br/>';
            echo "Setting pkgs: '".$this->package['name']."'.";
            echo '<hr/>';
            */
            $this->_circular_deps[$this->dep["#"]][]         = $this->package['name'];
            $this->_circular_pkgs[$this->package['name']][]  = $this->dep["#"];
        }
    }
    
    function _unsetCircular()
    {
        /*
        echo '<br/>';
        echo "Unsetting deps: '".$this->dep["#"]."'.";
        echo '<br/>';
        echo "Unsetting pkgs: '".$this->package['name']."'.";
        echo '<hr/>';
        */
        unset($this->_circular_deps[$this->dep["#"]]);
        unset($this->_circular_pkgs[$this->package['name']]);
    }
}
?>
Return current item: ZZ/OSS Installer