Index: build/manifests/jelix-lib.mn
===================================================================
--- build/manifests/jelix-lib.mn	(rÃ©vision 908)
+++ build/manifests/jelix-lib.mn	(copie de travail)
@@ -305,6 +305,7 @@
 cd lib/jelix/utils/
   jAppManager.class.php
   jClasses.class.php
+  jBinding.class.php
   jCmdUtils.class.php
   jCrypt.class.php
   jDatatype.class.php
Index: build/manifests/testapp.mn
===================================================================
--- build/manifests/testapp.mn	(rÃ©vision 908)
+++ build/manifests/testapp.mn	(copie de travail)
@@ -1,4 +1,5 @@
 cd testapp
+
   project.xml
   .htaccess
   application.init.php
@@ -86,6 +87,7 @@
 cd testapp/modules/jelix_tests/classes/tests
   foo.class.php
   foo.iface.php
+  bind.class.php
 cd testapp/modules/jelix_tests/locales/
   test_A.properties
   test_B.properties
@@ -173,6 +175,7 @@
   utils.jlog.html_cli.php
   utils.jdatetime.html_cli.php
   utils.jinifilemodifier.html_cli.php
+  utils.jclasses.html_cli.php
 
 cd testapp/var
   .htaccess
Index: testapp/modules/jelix_tests/tests/utils.jclasses.html_cli.php
===================================================================
--- testapp/modules/jelix_tests/tests/utils.jclasses.html_cli.php	(rÃ©vision 0)
+++ testapp/modules/jelix_tests/tests/utils.jclasses.html_cli.php	(rÃ©vision 0)
@@ -0,0 +1,132 @@
+<?php
+/**
+* @package     testapp
+* @subpackage  jelix_tests module
+* @author      Thiriot Christophe
+* @contributor
+* @copyright   2008 Thiriot Christophe
+* @link        http://www.jelix.org
+* @licence     GNU Lesser General Public Licence see LICENCE file or http://www.gnu.org/licenses/lgpl.html
+* @since 1.1
+*/
+
+class UTjclasses extends UnitTestCase {
+
+    public function setUp() {
+        jClasses::resetBindings();
+    }
+
+    public function testClassNoBinding() {
+        $this->assertTrue(class_exists('jBinding'));
+        $class = jClasses::getBindedService('class:jelix_tests~myclass');
+        $this->assertTrue($class instanceof myclass);
+
+        // same test with an interface (raises an exception)
+        try {
+            $class = jClasses::getBindedService('iface:jelix_tests~test');
+            $this->fail('An interface without binding should raise a jException');
+        } catch (jException $e) {
+            $this->pass();
+        }
+    }
+
+    public function testBindingInCodeTo() {
+        jClasses::bind('jelix_tests~test')->to('jelix_tests~myclass');
+        $class = jClasses::getBindedService('jelix_tests~test');
+        $this->assertTrue($class instanceof myclass);
+
+        jClasses::bind('jelix_tests~test')->to('jelix_tests~myclass');
+        $classname = jClasses::getBinding('jelix_tests~test')->getClassName();
+        $this->assertTrue($classname === 'myclass');
+
+        try {
+            jClasses::bind('jelix_tests~test')->to('jelix_tests~notexistingclass');
+            $this->fail('A binding to a non existing class should raise an exception');
+        } catch (jExceptionSelector $e) {
+            $this->pass();
+        }
+    }
+
+    public function testBindingInCodeToInstance() {
+        $instance = jClasses::create('jelix_tests~myclass');
+        jClasses::bind('jelix_tests~test')->toInstance($instance);
+        $class = jClasses::getBindedService('jelix_tests~test');
+        $this->assertTrue($class instanceof myclass);
+
+        jClasses::bind('jelix_tests~test')->toInstance($instance);
+        $classname = jClasses::getBinding('jelix_tests~test')->getClassName();
+        $this->assertTrue($classname === 'myclass');
+    }
+
+    // test with binding in jelix config file + get class name + non existing binded class
+    public function testBindingInJelixConfigFile() {
+        global $gJConfig;
+        $oldgjconfig = clone $gJConfig;
+
+        $gJConfig->Bindings['jelix_tests-test'] = 'jelix_tests~myclass';
+        $class = jClasses::getBindedService('jelix_tests~test');
+        $this->assertTrue($class instanceof myclass);
+
+        // test with long selector and test with parse_ini_file
+        $gJConfig->Bindings['class:jelix_tests-myclass'] = 'jelix_tests~myclass';
+        $class = jClasses::getBindedService('class:jelix_tests~myclass');
+        $this->assertTrue($class instanceof myclass);
+
+        $gJConfig = $oldgjconfig;
+    }
+
+    // test with binding in DEFAULT IMPLEMENTATION constant + get class name + non existing binded class
+    public function testBindingInDefaultImplementation() {
+        $classname = jClasses::getBinding('iface:jelix_tests~tests/foo')->getClassName();
+        $this->assertEqual($classname, 'bind');
+        jClasses::resetBindings();
+
+        $class = jClasses::getBindedService('iface:jelix_tests~tests/foo');
+        $this->assertTrue($class instanceof bind);
+
+        jClasses::resetBindings();
+
+        try {
+            $class = jClasses::getBindedService('class:jelix_tests~test/bind');
+            $this->fail('A non existing default implementation should raise an exception');
+        } catch (jExceptionSelector $e) {
+            $this->pass();
+        }
+    }
+
+    // getBindedService should give the same instance if called twice
+    public function testBindedServiceCalledTwice(){
+        $obj1 = jClasses::getBindedService('class:jelix_tests~myclass');
+        $obj2 = jClasses::getBindedService('class:jelix_tests~myclass');
+        $this->assertTrue($obj1 === $obj2);
+
+        jClasses::bind('jelix_tests~test')->to('jelix_tests~myclass');
+        $obj1 = jClasses::getBindedService('jelix_tests~test');
+        $obj2 = jClasses::getBindedService('jelix_tests~test');
+        $this->assertTrue($obj1 === $obj2);
+    }
+
+    // createBinded called twice should give different instances if called twice
+    public function testcreateBindedCalledTwice(){
+        $obj1 = jClasses::createBinded('class:jelix_tests~myclass');
+        $obj2 = jClasses::createBinded('class:jelix_tests~myclass');
+        $this->assertTrue($obj1 !== $obj2);
+
+        jClasses::bind('jelix_tests~test')->to('jelix_tests~myclass');
+        $obj1 = jClasses::createBinded('jelix_tests~test');
+        $obj2 = jClasses::createBinded('jelix_tests~test');
+        $this->assertTrue($obj1 !== $obj2);
+    }
+
+    // createBinded called twice with a bind()->toInstance() before give diffent instances , too.
+    // then toInstance has no effect... 
+    public function testCreateBindedWithToInstanceCalledTwice(){
+        $instance = new StdClass();
+        jClasses::bind('jelix_tests~test')->toInstance($instance);
+        $obj1 = jClasses::createBinded('jelix_tests~test');
+        $obj2 = jClasses::createBinded('jelix_tests~test');
+        $this->assertTrue($obj1 !== $obj2);
+    }
+}
+
+?>
Index: testapp/modules/jelix_tests/classes/tests/foo.iface.php
===================================================================
--- testapp/modules/jelix_tests/classes/tests/foo.iface.php	(rÃ©vision 908)
+++ testapp/modules/jelix_tests/classes/tests/foo.iface.php	(copie de travail)
@@ -1,7 +1,7 @@
 <?php
 
 interface foo {
-
+    const JBINDING_BINDED_IMPLEMENTATION = 'jelix_tests~tests/bind';
 }
 
-?>
\ No newline at end of file
+?>
Index: testapp/modules/jelix_tests/classes/tests/bind.class.php
===================================================================
--- testapp/modules/jelix_tests/classes/tests/bind.class.php	(rÃ©vision 0)
+++ testapp/modules/jelix_tests/classes/tests/bind.class.php	(rÃ©vision 0)
@@ -0,0 +1,7 @@
+<?php
+
+class bind {
+    const JBINDING_BINDED_IMPLEMENTATION = 'jelix_tests~nonexistingclass';
+}
+
+?>
Index: lib/jelix/core/jSelector.class.php
===================================================================
--- lib/jelix/core/jSelector.class.php	(rÃ©vision 908)
+++ lib/jelix/core/jSelector.class.php	(copie de travail)
@@ -34,10 +34,14 @@
      * @param string $selstr  the selector. It should be a full selector : "type:module~resource" (not "module~resource")
      * @return jISelector the corresponding selector
      */
-    static public function create ($selstr){
-        if(preg_match("/^([a-z]{3,5})\:([\w~\/\.]+)$/", $selstr, $m)){
+    static public function create ($selstr, $defaulttype=false) {
+        if (is_string($defaulttype) && strpos($selstr, ':') === false) {
+            $selstr = "$defaulttype:$selstr";
+        }
+
+        if(preg_match("/^([a-z]{3,5})\:([\w~\/\.]+)$/", $selstr, $m)) {
             $cname='jSelector'.$m[1];
-            if(class_exists($cname)){
+            if(class_exists($cname)) {
                 $sel = new $cname($m[2]);
                 return $sel;
             }
Index: lib/jelix/CREDITS
===================================================================
--- lib/jelix/CREDITS	(rÃ©vision 908)
+++ lib/jelix/CREDITS	(copie de travail)
@@ -64,6 +64,7 @@
  - testor
  - bug fixes on PDO support in jDb
  - fixed minor bugs
+ - Added enhanced service handling (dependency injection) in jClasses (#523)
 
 
 Contributors
Index: lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-15.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-15.properties	(rÃ©vision 908)
+++ lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-15.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/en_EN/errors.UTF-8.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_EN/errors.UTF-8.properties	(rÃ©vision 908)
+++ lib/jelix/core-modules/jelix/locales/en_EN/errors.UTF-8.properties	(copie de travail)
@@ -110,5 +110,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-1.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-1.properties	(rÃ©vision 908)
+++ lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-1.properties	(copie de travail)
@@ -110,5 +110,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-15.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-15.properties	(rÃ©vision 908)
+++ lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-15.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/en_US/errors.UTF-8.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_US/errors.UTF-8.properties	(rÃ©vision 908)
+++ lib/jelix/core-modules/jelix/locales/en_US/errors.UTF-8.properties	(copie de travail)
@@ -110,5 +110,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-1.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-1.properties	(rÃ©vision 908)
+++ lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-1.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-15.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-15.properties	(rÃ©vision 908)
+++ lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-15.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s pour jAcl non trouvé
 acl.action.right.needed = (371) L'utilisateur n'a pas les droits pour cette action
 
+#---- bindings
+bindings.nobinding = (380) Le binding pour %s n'est pas défini
+
 #---- datetime
 datetime.invalid = (400)jDateTime: date/heure invalide (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/fr_FR/errors.UTF-8.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/fr_FR/errors.UTF-8.properties	(rÃ©vision 908)
+++ lib/jelix/core-modules/jelix/locales/fr_FR/errors.UTF-8.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s pour jAcl non trouvÃ©
 acl.action.right.needed = (371) L'utilisateur n'a pas les droits pour cette action
 
+#---- bindings
+bindings.nobinding = (380) Le binding pour %s n'est pas dÃ©fini
+
 #---- datetime
 datetime.invalid = (400)jDateTime: date/heure invalide (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-1.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-1.properties	(rÃ©vision 908)
+++ lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-1.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s pour jAcl non trouvé
 acl.action.right.needed = (371) L'utilisateur n'a pas les droits pour cette action
 
+#---- bindings
+bindings.nobinding = (380) Le binding pour %s n'est pas défini
+
 #---- datetime
 datetime.invalid = (400)jDateTime: date/heure invalide (%d-%d-%d %d:%d:%d)
Index: lib/jelix/utils/jClasses.class.php
===================================================================
--- lib/jelix/utils/jClasses.class.php	(rÃ©vision 908)
+++ lib/jelix/utils/jClasses.class.php	(copie de travail)
@@ -19,6 +19,8 @@
 
     static protected $_instances = array();
 
+    static protected $_bindings = array();
+
     private function __construct(){}
 
     /**
@@ -26,7 +28,7 @@
      * @param string $selector the jelix selector correponding to the class
      * @return object an instance of the classe
      */
-    static public function create($selector){
+    static public function create($selector) {
         $sel = new jSelectorClass($selector);
         require_once($sel->getPath());
         $class = $sel->className;
@@ -34,10 +36,22 @@
     }
 
     /**
+     * Shortcut to corresponding jBinding::getInstance() but without singleton
+     * The binding is recreated each time (be careful about performance)
+     * 
+     * @param string $selector  Selector to a bindable class|interface
+     * @return object           Corresponding instance
+     * @since 1.1
+     */
+    static public function createBinded($selector) {
+        return self::getBinding($selector, false);
+    }
+
+    /**
      * alias of create method
      * @see jClasses::create()
      */
-    static public function createInstance($selector){
+    static public function createInstance($selector) {
         return self::create($selector);
     }
 
@@ -47,7 +61,7 @@
      * @param string $selector the jelix selector correponding to the class
      * @return object an instance of the classe
      */
-    static public function getService($selector){
+    static public function getService($selector) {
         $sel = new jSelectorClass($selector);
         $s = $sel->toString();
         if (isset(self::$_instances[$s])) {
@@ -60,6 +74,65 @@
     }
 
     /**
+     * Shortcut to corresponding jBinding::getInstance() 
+     * 
+     * @param string $selector  Selector to a bindable class|interface
+     * @return object           Corresponding instance
+     * @since 1.1
+     */
+    static public function getBindedService($selector) {
+        return self::getBinding($selector)->getInstance();
+    }
+
+    /**
+     * Alias of self::getBinding method. Better for use like this : jClasses::bind('selector').to('classselector')
+     * 
+     * @param  string $selector 
+     * @return jBinding
+     * @see jClasses::bind
+     * @since 1.1
+     */
+    static public function bind($selector) {
+        return self::getBinding($selector);
+    }
+
+    /**
+     * Get the binding corresponding to the specified selector.
+     * Better for use like this : jClasses::getBinding($selector)->getClassName()
+     * 
+     * @param string $selector
+     * @param bool   $singleton if this binding should be a singleton or not
+     * @return jBinding
+     * @see jClasses::bind
+     * @since 1.1
+     */
+    static public function getBinding($selector, $singleton=true) {
+        $osel = jSelectorFactory::create($selector, 'iface');
+        $s    = $osel->toString(true);
+
+        $binding = null;
+        if ($singleton === false || !isset(self::$_bindings[$s])) {
+            $binding = new jBinding($osel);
+            if ($singleton === true) self::$_bindings[$s] = $binding;
+        } else {
+            $binding = self::$_bindings[$s];
+        }
+
+
+        return $binding;
+    }
+
+    /**
+     * Reset the defined bindings (should only use it for unit tests)
+     * 
+     * @return void
+     * @since 1.1
+     */
+    static public function resetBindings() {
+        self::$_bindings = array();
+    }
+
+    /**
      * only include a class
      * @param string $selector the jelix selector correponding to the class
      */
Index: lib/jelix/utils/jBinding.class.php
===================================================================
--- lib/jelix/utils/jBinding.class.php	(rÃ©vision 0)
+++ lib/jelix/utils/jBinding.class.php	(rÃ©vision 0)
@@ -0,0 +1,151 @@
+<?php
+/**
+ * @package     jelix
+ * @subpackage  utils
+ * @author      Christophe THIRIOT
+ * @copyright   2008 Christophe THIRIOT
+ * @link        http://www.jelix.org
+ * @licence     GNU Lesser General Public Licence see LICENCE file or http://www.gnu.org/licenses/lgpl.html
+ * @since 1.1
+ */
+
+/**
+ * Services binding for jelix
+ *
+ * @package     jelix
+ * @subpackage  utils
+ */
+class jBinding {
+    /**
+     * @var jSelectorInterface|jSelectorClass Called selector
+     */
+    protected $fromSelector = null;
+
+    /**
+     * selector to the binded class (string form)
+     */
+    protected $toSelector = null;
+
+    /**
+     * resulting binded instance
+     */
+    protected $instance = null;
+
+    /**
+     * __constructor
+     * @param jSelectorInterface|jSelectorClass
+     * @return void
+     */
+    public function __construct($selector) {
+        require_once($selector->getPath());
+        $this->fromSelector = $selector;
+    }
+
+    /**
+     * Bind the selector to the class specified
+     * Even if this instance is already defined (BE CAREFUL !!! singleton is bypassed)
+     *
+     * @param string $toselector
+     * @return jBinding $this
+     */
+    public function to($toselector) {
+        $this->toSelector = new jSelectorClass($toselector);
+        $this->instance   = null;
+        return $this;
+    }
+
+    /**
+     * Bind the selector to the specified instance
+     * Even if this instance is already defined (BE CAREFUL !!! singleton is bypassed)
+     *
+     * @param  mixed    $instance
+     * @return jBinding $this
+     */
+    public function toInstance($instance) {
+        $this->instance   = $instance;
+        $this->toSelector = null;
+        return $this;
+    }
+
+    /**
+     * Get the binded instance
+     *
+     * @return mixed
+     */
+    public function getInstance() {
+        if ($this->instance === null) {
+            if ($this->toSelector === null) {
+                $this->toSelector = $this->_getClassSelector();
+            }
+            $this->instance = jClasses::create($this->toSelector->toString());
+        }
+        return $this->instance;
+    }
+
+    /**
+     * Get the name of the binded class without creating this class
+     *
+     * @return string
+     */
+    public function getClassName() {
+        $class_name = null;
+        if ($this->instance !== null) {
+            $class_name = get_class($this->instance);
+        } elseif ($this->toSelector !== null) {
+            $class_name = $this->toSelector->className;
+        } else {
+            $class_name = $this->_getClassSelector()->className;
+        }
+        return $class_name;
+    }
+
+    /**
+     * Get the selector to the binded class
+     * Protected because this does not work if called after a simple __construct() and a toInstance()
+     *
+     * @return string
+     */
+    protected function _getClassSelector() {
+        $class_selector = null;
+
+        // the instance is not already created
+        if ($this->toSelector === null && $this->instance === null) {
+            $str_selector      = $this->fromSelector->toString();
+            $str_selector_long = $this->fromSelector->toString(true);
+
+            // 1) verify that a default implementation is specified in the jelix config file
+            global $gJConfig;
+            if (isset($gJConfig->Bindings)) {
+                $conf = $gJConfig->Bindings;
+
+                // No '~' allowed as key of a ini file, we use '-' instead
+                $conf_selector      = str_replace('~', '-', $str_selector);
+                $conf_selector_long = str_replace('~', '-', $str_selector_long);
+                // get the binding corresponding to selector, long or not
+                $str_fromselector = null;
+                if (isset($conf[$conf_selector])) {
+                    $str_fromselector = $conf_selector;
+                } elseif (isset($conf[$conf_selector_long])) {
+                    $str_fromselector = $conf_selector_long;
+                }
+
+                if ($str_fromselector !== null) {
+                    $this->fromSelector = jSelectorFactory::create($str_selector_long, 'iface');
+                    return $this->toSelector = new jSelectorClass($conf[$str_fromselector]);
+                }
+            }
+
+            // 2) see if a default implementation is specified in the source class
+            $class_selector = @constant($this->fromSelector->className . '::JBINDING_BINDED_IMPLEMENTATION');
+            if ($class_selector!==null) return $this->toSelector = new jSelectorClass($class_selector);
+
+            // 3) If the source is a class, then use it as the default implementation
+            if (true === ($this->fromSelector instanceof jSelectorClass)) {
+                return $this->toSelector = $this->fromSelector;
+            }
+
+            throw new jException('jelix~errors.bindings.nobinding', array($this->fromSelector->toString(true)));
+        }
+    }
+}
+?>
