Ticket #523: bindings.patch

File bindings.patch, 14.6 kB (added by doubleface, 2 years ago)
  • build/manifests/jelix-lib.mn

     
    265265cd lib/jelix/utils/ 
    266266  jAppManager.class.php 
    267267  jClasses.class.php 
     268  jBinding.class.php 
    268269  jCmdUtils.class.php 
    269270  jCrypt.class.php 
    270271  jDatatype.class.php 
  • build/manifests/testapp.mn

     
    11cd testapp 
     2 
    23  project.xml 
    34  .htaccess 
    45  application.init.php 
     
    8182cd testapp/modules/jelix_tests/classes/tests 
    8283  foo.class.php 
    8384  foo.iface.php 
     85  bind.class.php 
    8486cd testapp/modules/jelix_tests/locales/ 
    8587  test_A.properties 
    8688  test_B.properties 
     
    163165  utils.jlog.html_cli.php 
    164166  utils.jdatetime.html_cli.php 
    165167  utils.jinifilemodifier.html_cli.php 
     168  utils.jclasses.html_cli.php 
    166169 
    167170cd testapp/var 
    168171  .htaccess 
  • testapp/modules/jelix_tests/tests/utils.jclasses.html_cli.php

     
     1<?php 
     2/** 
     3* @package     testapp 
     4* @subpackage  jelix_tests module 
     5* @author      Thiriot Christophe 
     6* @contributor 
     7* @copyright   2008 Thiriot Christophe 
     8* @link        http://www.jelix.org 
     9* @licence     GNU Lesser General Public Licence see LICENCE file or http://www.gnu.org/licenses/lgpl.html 
     10*/ 
     11 
     12class UTjclasses extends UnitTestCase { 
     13 
     14    public function setUp() { 
     15        jClasses::resetBindings(); 
     16    } 
     17 
     18    public function testClassNoBinding() { 
     19        $this->assertTrue(class_exists('jBinding')); 
     20        $class = jClasses::getBindedService('class:jelix_tests~myclass'); 
     21        $this->assertTrue($class instanceof myclass); 
     22 
     23        // same test with an interface (raises an exception) 
     24        try { 
     25            $class = jClasses::getBindedService('iface:jelix_tests~test'); 
     26            $this->fail('An interface without binding should raise an exception'); 
     27        } catch (jExceptionSelector $e) { 
     28            $this->pass(); 
     29        } 
     30    } 
     31 
     32    public function testBindingInCodeTo() { 
     33        jClasses::bind('jelix_tests~test')->to('jelix_tests~myclass'); 
     34        $class = jClasses::getBindedService('jelix_tests~test'); 
     35        $this->assertTrue($class instanceof myclass); 
     36 
     37        jClasses::bind('jelix_tests~test')->to('jelix_tests~myclass'); 
     38        $classname = jClasses::getBinding('jelix_tests~test')->getClassName(); 
     39        $this->assertTrue($classname === 'myclass'); 
     40 
     41        try { 
     42            jClasses::bind('jelix_tests~test')->to('jelix_tests~notexistingclass'); 
     43            $this->fail('A binding to a non existing class should raise an exception'); 
     44        } catch (jExceptionSelector $e) { 
     45            $this->pass(); 
     46        } 
     47    } 
     48 
     49    public function testBindingInCodeToInstance() { 
     50        $instance = jClasses::create('jelix_tests~myclass'); 
     51        jClasses::bind('jelix_tests~test')->toInstance($instance); 
     52        $class = jClasses::getBindedService('jelix_tests~test'); 
     53        $this->assertTrue($class instanceof myclass); 
     54 
     55        jClasses::bind('jelix_tests~test')->toInstance($instance); 
     56        $classname = jClasses::getBinding('jelix_tests~test')->getClassName(); 
     57        $this->assertTrue($classname === 'myclass'); 
     58    } 
     59 
     60    // test with binding in jelix config file + get class name + non existing binded class 
     61    public function testBindingInJelixConfigFile() { 
     62        global $gJConfig; 
     63        $oldgjconfig = clone $gJConfig; 
     64 
     65        $gJConfig->{jBinding::BINDINGS_CONFIG_SECTION}['jelix_tests*test'] = 'jelix_tests~myclass'; 
     66        $class = jClasses::getBindedService('jelix_tests~test'); 
     67        $this->assertTrue($class instanceof myclass); 
     68 
     69        // test with long selector and test with parse_ini_file 
     70        $gJConfig->{jBinding::BINDINGS_CONFIG_SECTION}['class:jelix_tests*myclass'] = 'jelix_tests~myclass'; 
     71        $class = jClasses::getBindedService('class:jelix_tests~myclass'); 
     72        $this->assertTrue($class instanceof myclass); 
     73 
     74        $gJConfig = $oldgjconfig; 
     75    } 
     76 
     77    // test with binding in DEFAULT IMPLEMENTATION constant + get class name + non existing binded class 
     78    public function testBindingInDefaultImplementation() { 
     79        $classname = jClasses::getBinding('iface:jelix_tests~tests/foo')->getClassName(); 
     80        $this->assertEqual($classname, 'bind'); 
     81        jClasses::resetBindings(); 
     82 
     83        $class = jClasses::getBindedService('iface:jelix_tests~tests/foo'); 
     84        $this->assertTrue($class instanceof bind); 
     85 
     86        jClasses::resetBindings(); 
     87 
     88        try { 
     89            $class = jClasses::getBindedService('class:jelix_tests~test/bind'); 
     90            $this->fail('A non existing default implementation should raise an exception'); 
     91        } catch (jExceptionSelector $e) { 
     92            $this->pass(); 
     93        } 
     94    } 
     95} 
     96 
     97?> 
  • testapp/modules/jelix_tests/classes/tests/foo.iface.php

     
    11<?php 
    22 
    33interface foo { 
    4  
     4    const JBINDING_BINDED_IMPLEMENTATION = 'jelix_tests~tests/bind'; 
    55} 
    66 
    7 ?> 
    8  Pas de fin de ligne à la fin du fichier 
     7?> 
  • testapp/modules/jelix_tests/classes/tests/bind.class.php

     
     1<?php 
     2 
     3class bind { 
     4    const JBINDING_BINDED_IMPLEMENTATION = 'jelix_tests~nonexistingclass'; 
     5} 
     6 
     7?> 
  • lib/jelix/core/jSelector.class.php

     
    3434     * @param string $selstr  the selector. It should be a full selector : "type:module~resource" (not "module~resource") 
    3535     * @return jISelector the corresponding selector 
    3636     */ 
    37     static public function create ($selstr){ 
     37    static public function create ($selstr, $defaulttype=false){ 
     38        if (is_string($defaulttype) && strpos($selstr, ':') === false) { 
     39            $selstr = "$defaulttype:$selstr"; 
     40        } 
     41 
    3842        if(preg_match("/^([a-z]{3,5})\:([\w~\/\.]+)$/", $selstr, $m)){ 
    3943            $cname='jSelector'.$m[1]; 
    4044            if(class_exists($cname)){ 
  • lib/jelix/utils/jClasses.class.php

     
    1919 
    2020    static protected $_instances = array(); 
    2121 
     22    static protected $_bindings = array(); 
     23 
    2224    private function __construct(){} 
    2325 
    2426    /** 
     
    6062    } 
    6163 
    6264    /** 
     65     * Shortcut to corresponding jBinding::getInstance()  
     66     *  
     67     * @param string $selector  Selector to a bindable class|interface 
     68     * @return mixed            Corresponding instance 
     69     */ 
     70    static public function getBindedService($selector){ 
     71        return self::getBinding($selector)->getInstance(); 
     72    } 
     73 
     74    /** 
     75     * Alias of self::getBinding method. Better for use like this : jClasses::bind('selector').to('classselector') 
     76     *  
     77     * @param  string $selector  
     78     * @return jBinding 
     79     * @see jClasses::bind 
     80     */ 
     81    static public function bind($selector) { 
     82        return self::getBinding($selector); 
     83    } 
     84 
     85    /** 
     86     * Get the binding corresponding to the specified selector. 
     87     * Better for use like this : jClasses::getBinding($selector)->getClassName() 
     88     *  
     89     * @param string $selector  
     90     * @return jBinding 
     91     * @see jClasses::bind 
     92     */ 
     93    static public function getBinding($selector) { 
     94        $osel = jSelectorFactory::create($selector, 'iface'); 
     95        $s    = $osel->toString(true); 
     96 
     97        if (!isset(self::$_bindings[$s])) { 
     98            self::$_bindings[$s] = new jBinding($osel); 
     99        } 
     100 
     101        return self::$_bindings[$s]; 
     102    } 
     103 
     104    /** 
     105     * Reset the defined bindings (should only use it for unit tests) 
     106     *  
     107     * @return void 
     108     */ 
     109    static public function resetBindings() { 
     110        self::$_bindings = array(); 
     111    } 
     112 
     113    /** 
    63114     * only include a class 
    64115     * @param string $selector the jelix selector correponding to the class 
    65116     */ 
  • lib/jelix/utils/jBinding.class.php

     
     1<?php 
     2/** 
     3 * @package     jelix 
     4 * @subpackage  utils 
     5 * @author      Christophe THIRIOT 
     6 * @copyright   2008 Christophe THIRIOT  
     7 * @link        http://www.jelix.org 
     8 * @licence     GNU Lesser General Public Licence see LICENCE file or http://www.gnu.org/licenses/lgpl.html 
     9 */ 
     10 
     11/** 
     12 * Services binding for jelix 
     13 * 
     14 * @package     jelix 
     15 * @subpackage  utils 
     16 */ 
     17class jBinding 
     18{ 
     19    /** 
     20     *  Section in the jelix config file where bindings are specified 
     21     */ 
     22    const BINDINGS_CONFIG_SECTION = 'Bindings'; 
     23 
     24    /** 
     25     * Name of the constant setting the default binded implementation 
     26     */ 
     27    const BINDED_IMPLEMENTATION_LABEL = 'JBINDING_BINDED_IMPLEMENTATION'; 
     28 
     29    /** 
     30     * Jelix selector separator in code 
     31     */ 
     32    const CODE_SELECTOR_SEPARATOR = '~'; 
     33 
     34    /** 
     35     * Jelix selector separator in jelix config file  
     36     */ 
     37    const CONFIG_SELECTOR_SEPARATOR = '*'; 
     38 
     39    /** 
     40     * @var jSelectorInterface|jSelectorClass Called selector  
     41     */ 
     42    protected $fromselector = null; 
     43 
     44    /** 
     45     * selector to the binded class (string form)  
     46     */ 
     47    protected $toselector = null; 
     48 
     49    /** 
     50     * resulting binded instance 
     51     */ 
     52    protected $instance = null; 
     53 
     54    /** 
     55     * __constructor  
     56     * @param jSelectorInterface|jSelectorClass 
     57     * @return void 
     58     */ 
     59    public function __construct($selector) { 
     60        require_once($selector->getPath()); 
     61        $this->fromselector = $selector; 
     62    } 
     63 
     64    /** 
     65     * Bind the selector to the class specified 
     66     * Even if this instance is already defined (BE CAREFUL !!! singleton is bypassed) 
     67     *  
     68     * @param string $toselector  
     69     * @return jBinding $this 
     70     */ 
     71    public function to($toselector) { 
     72        $this->toselector = new jSelectorClass($toselector); 
     73        $this->instance   = null; 
     74        return $this; 
     75    } 
     76 
     77    /** 
     78     * Bind the selector to the specified instance 
     79     * Even if this instance is already defined (BE CAREFUL !!! singleton is bypassed) 
     80     *  
     81     * @param  mixed    $instance  
     82     * @return jBinding $this 
     83     */ 
     84    public function toInstance($instance) { 
     85        $this->instance   = $instance; 
     86        $this->toselector = null; 
     87        return $this; 
     88    } 
     89 
     90    /** 
     91     * Get the binded instance 
     92     *  
     93     * @return mixed 
     94     */ 
     95    public function getInstance() { 
     96        if ($this->instance === null){ 
     97            if ($this->toselector === null) { 
     98                $this->toselector = $this->_getClassSelector();                 
     99            } 
     100            $this->instance = jClasses::create($this->toselector->toString()); 
     101        } 
     102        return $this->instance; 
     103    } 
     104 
     105    /** 
     106     * Get the name of the binded class without creating this class 
     107     *  
     108     * @return string 
     109     */ 
     110    public function getClassName() { 
     111        $class_name = null; 
     112        if ($this->instance !== null) { 
     113            $class_name = get_class($this->instance); 
     114        } elseif ($this->toselector !== null) { 
     115            $class_name = $this->toselector->className; 
     116        } else { 
     117            $class_name = $this->_getClassSelector()->className; 
     118        } 
     119        return $class_name; 
     120    } 
     121 
     122    /** 
     123     * Get the selector to the binded class  
     124     * Protected because this does not work if called after a simple __construct() and a toInstance() 
     125     *  
     126     * @return string 
     127     */ 
     128    protected function _getClassSelector() { 
     129        $class_selector = null; 
     130 
     131        // the instance is not already created 
     132        if ($this->toselector === null && $this->instance === null) { 
     133            $str_selector      = $this->fromselector->toString(); 
     134            $str_selector_long = $this->fromselector->toString(true); 
     135 
     136            // 1) verify that a default implementation is specified in the jelix config file 
     137            global $gJConfig; 
     138            if (isset($gJConfig->{self::BINDINGS_CONFIG_SECTION})) { 
     139                $conf = $gJConfig->{self::BINDINGS_CONFIG_SECTION}; 
     140                 
     141                // No '~' allowed as key of a ini file, we use '*' instead 
     142                $conf_selector      = str_replace(self::CODE_SELECTOR_SEPARATOR, self::CONFIG_SELECTOR_SEPARATOR, $str_selector); 
     143                $conf_selector_long = str_replace(self::CODE_SELECTOR_SEPARATOR, self::CONFIG_SELECTOR_SEPARATOR, $str_selector_long); 
     144                // get the binding corresponding to selctor, long or not 
     145                $str_fromselector = null; 
     146                if (isset($conf[$conf_selector])) { 
     147                    $str_fromselector = $conf_selector; 
     148                } elseif (isset($conf[$conf_selector_long])){ 
     149                    $str_fromselector = $conf_selector_long; 
     150                } 
     151 
     152                if ($str_fromselector !== null) { 
     153                    $this->fromselector = jSelectorFactory::create($str_selector_long, 'interface'); 
     154                    return $this->toselector = new jSelectorClass($conf[$str_fromselector]); 
     155                } 
     156            } 
     157 
     158            // 2) see if a default implementation is specified in the source class 
     159            $class_selector = @constant($this->fromselector->className . '::' . jBinding::BINDED_IMPLEMENTATION_LABEL); 
     160            if ($class_selector!==null) return $this->toselector = new jSelectorClass($class_selector); 
     161 
     162            // 3) If the source is a class, then use it as the default implementation 
     163            if ($this->fromselector instanceof jSelectorClass) { 
     164                return $this->toselector = new jSelectorClass($str_selector); 
     165            } 
     166             
     167            // TODO add this locale in jelix locales 
     168            throw new jException('jelix~errors.binding.nobinding', array($this->fromselector->toString(true))); 
     169        } 
     170    } 
     171} 
     172?>