diff --git a/lib/action/sfActions.class.php b/lib/action/sfActions.class.php index fcc84b47c..b2d497604 100644 --- a/lib/action/sfActions.class.php +++ b/lib/action/sfActions.class.php @@ -4,7 +4,7 @@ * This file is part of the symfony package. * (c) 2004-2006 Fabien Potencier * (c) 2004-2006 Sean Kerr - * + * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ @@ -57,6 +57,9 @@ public function execute($request) } // run action - return $this->$actionToRun($request); + return $this->getService('sf_parameter_resolver') + ->setRequest($request) + ->setComponent($this) + ->execute($actionToRun); } } diff --git a/lib/action/sfParameterResolver.class.php b/lib/action/sfParameterResolver.class.php new file mode 100644 index 000000000..0af9874b8 --- /dev/null +++ b/lib/action/sfParameterResolver.class.php @@ -0,0 +1,70 @@ +container = $container; + } + + public function setRequest(sfWebRequest $request) + { + $this->request = $request; + + return $this; + } + + public function setComponent(sfComponent $component) + { + $this->component = $component; + + return $this; + } + + public function execute($actionToRun = 'execute') + { + return call_user_func_array(array($this->component, $actionToRun), $this->resolveParams($actionToRun)); + } + + protected function resolveParams($actionToRun) + { + $reflection = new ReflectionObject($this->component); + $method = $reflection->getMethod($actionToRun); + + $parameters = array(); + foreach ($method->getParameters() as $i => $param) { + $type = $param->getClass(); + + if (null === $type) { + if ($i === 0) { + // first parameter is always the request + $parameters[] = $this->request; + continue; + } elseif ($param->getDefaultValue()) { + // additional params with default values may have been added + $params[] = $param->getDefaultValue(); + continue; + } else { + throw new \Exception("Additional parameters must be type hinted or provide a default value"); + } + } + + if ($type->getName() == "sfWebRequest") { + $parameters[] = $this->request; + } else { + $parameters[] = $this->getParamFromContainer($type->getName()); + } + } + + return $parameters; + } + + protected function getParamFromContainer($param) + { + return $this->container->getService($param); + } +} diff --git a/lib/autoload/sfCoreAutoload.class.php b/lib/autoload/sfCoreAutoload.class.php index d16a0310b..dbf596dc7 100755 --- a/lib/autoload/sfCoreAutoload.class.php +++ b/lib/autoload/sfCoreAutoload.class.php @@ -187,6 +187,7 @@ static public function make() 'sfactions' => 'action/sfActions.class.php', 'sfcomponent' => 'action/sfComponent.class.php', 'sfcomponents' => 'action/sfComponents.class.php', + 'sfparameterresolver' => 'action/sfParameterResolver.class.php', 'sfdata' => 'addon/sfData.class.php', 'sfpager' => 'addon/sfPager.class.php', 'sfautoload' => 'autoload/sfAutoload.class.php', @@ -522,8 +523,8 @@ static public function make() 'sfwidgetforminput' => 'widget/sfWidgetFormInput.class.php', 'sfwidgetforminputcheckbox' => 'widget/sfWidgetFormInputCheckbox.class.php', 'sfwidgetforminputfile' => 'widget/sfWidgetFormInputFile.class.php', - 'sfwidgetforminputfilemulti' => 'widget/sfWidgetFormInputFileMulti.class.php', 'sfwidgetforminputfileeditable' => 'widget/sfWidgetFormInputFileEditable.class.php', + 'sfwidgetforminputfilemulti' => 'widget/sfWidgetFormInputFileMulti.class.php', 'sfwidgetforminputhidden' => 'widget/sfWidgetFormInputHidden.class.php', 'sfwidgetforminputpassword' => 'widget/sfWidgetFormInputPassword.class.php', 'sfwidgetforminputread' => 'widget/sfWidgetFormInputRead.class.php', diff --git a/lib/config/config/services.yml b/lib/config/config/services.yml index 25db23634..28f7f4103 100644 --- a/lib/config/config/services.yml +++ b/lib/config/config/services.yml @@ -17,3 +17,8 @@ all: arguments: - '@sf_event_dispatcher' - '@sf_formatter' + + sf_parameter_resolver: + class: sfParameterResolver + arguments: + - '@service_container' diff --git a/lib/filter/sfExecutionFilter.class.php b/lib/filter/sfExecutionFilter.class.php index 828c04db2..d8220bc03 100644 --- a/lib/filter/sfExecutionFilter.class.php +++ b/lib/filter/sfExecutionFilter.class.php @@ -90,7 +90,11 @@ protected function executeAction($actionInstance) { // execute the action $actionInstance->preExecute(); - $viewName = $actionInstance->execute($this->context->getRequest()); + $viewName = $this->context->getService('sf_parameter_resolver') + ->setRequest($this->context->getRequest()) + ->setComponent($actionInstance) + ->execute(); + $actionInstance->postExecute(); return null === $viewName ? sfView::SUCCESS : $viewName; diff --git a/lib/helper/PartialHelper.php b/lib/helper/PartialHelper.php index e20d0b9c2..2d21941f5 100644 --- a/lib/helper/PartialHelper.php +++ b/lib/helper/PartialHelper.php @@ -84,7 +84,7 @@ function has_component_slot($name) { return false; } - + // check to see if component slot is empty (null) if ($viewInstance->getComponentSlot($name)) { @@ -385,7 +385,10 @@ function _call_component($moduleName, $componentName, $vars) $timer = sfTimerManager::getTimer(sprintf('Component "%s/%s"', $moduleName, $componentName)); } - $retval = $componentInstance->$componentToRun($context->getRequest()); + $retval = $context->getService('sf_parameter_resolver') + ->setRequest($context->getRequest()) + ->setComponent($componentInstance) + ->execute($componentToRun); if (sfConfig::get('sf_debug') && sfConfig::get('sf_logging_enabled')) { diff --git a/test/functional/autowiringTest.php b/test/functional/autowiringTest.php new file mode 100644 index 000000000..f71a96903 --- /dev/null +++ b/test/functional/autowiringTest.php @@ -0,0 +1,34 @@ + + get('/autowiring/index')-> + with('request')->begin()-> + isParameter('module', 'autowiring')-> + isParameter('action', 'index')-> + end()-> + with('response')->begin()-> + isStatusCode(200)-> + checkElement('div[id=value]', 'success')-> + end() +; + +// test component execution +$b-> + get('/autowiring/withComponents')-> + with('request')->begin()-> + isParameter('module', 'autowiring')-> + isParameter('action', 'withComponents')-> + end()-> + with('response')->begin()-> + isStatusCode(200)-> + checkElement('div[id=component_value]', 'success')-> + end() +; diff --git a/test/functional/fixtures/apps/frontend/config/services.yml b/test/functional/fixtures/apps/frontend/config/services.yml index abf6f0e92..95a571e67 100644 --- a/test/functional/fixtures/apps/frontend/config/services.yml +++ b/test/functional/fixtures/apps/frontend/config/services.yml @@ -7,6 +7,10 @@ all: class: sfOutputEscaperSafe arguments: [%my_project_param%] + MyService: + class: MyService + + test: parameters: my_app_test_param: foo diff --git a/test/functional/fixtures/apps/frontend/modules/autowiring/actions/actions.class.php b/test/functional/fixtures/apps/frontend/modules/autowiring/actions/actions.class.php new file mode 100644 index 000000000..b06cec199 --- /dev/null +++ b/test/functional/fixtures/apps/frontend/modules/autowiring/actions/actions.class.php @@ -0,0 +1,16 @@ +value = $service->execute(); + return sfView::SUCCESS; + } + + public function executeWithComponents($request) + { + return sfView::SUCCESS; + } +} diff --git a/test/functional/fixtures/apps/frontend/modules/autowiring/actions/components.class.php b/test/functional/fixtures/apps/frontend/modules/autowiring/actions/components.class.php new file mode 100644 index 000000000..f6ce7ff78 --- /dev/null +++ b/test/functional/fixtures/apps/frontend/modules/autowiring/actions/components.class.php @@ -0,0 +1,10 @@ +value = $service->execute(); + } +} diff --git a/test/functional/fixtures/apps/frontend/modules/autowiring/lib/MyService.class.php b/test/functional/fixtures/apps/frontend/modules/autowiring/lib/MyService.class.php new file mode 100644 index 000000000..f5a5e2851 --- /dev/null +++ b/test/functional/fixtures/apps/frontend/modules/autowiring/lib/MyService.class.php @@ -0,0 +1,10 @@ + diff --git a/test/functional/fixtures/apps/frontend/modules/autowiring/templates/indexSuccess.php b/test/functional/fixtures/apps/frontend/modules/autowiring/templates/indexSuccess.php new file mode 100644 index 000000000..54e539387 --- /dev/null +++ b/test/functional/fixtures/apps/frontend/modules/autowiring/templates/indexSuccess.php @@ -0,0 +1 @@ +
diff --git a/test/functional/fixtures/apps/frontend/modules/autowiring/templates/withComponentsSuccess.php b/test/functional/fixtures/apps/frontend/modules/autowiring/templates/withComponentsSuccess.php new file mode 100644 index 000000000..f6cd64f6b --- /dev/null +++ b/test/functional/fixtures/apps/frontend/modules/autowiring/templates/withComponentsSuccess.php @@ -0,0 +1 @@ + diff --git a/test/unit/action/sfParameterResolverTest.php b/test/unit/action/sfParameterResolverTest.php new file mode 100644 index 000000000..c211681c3 --- /dev/null +++ b/test/unit/action/sfParameterResolverTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please component the LICENSE + * file that was distributed with this source code. + */ + +require_once(__DIR__.'/../../bootstrap/unit.php'); +require_once($_test_dir.'/unit/sfContextMock.class.php'); +require_once($_test_dir.'/unit/sfNoRouting.class.php'); + +$t = new lime_test(3); + +class MyService { + public function execute() + { + return "success"; + } +} + +class myComponent extends sfComponent +{ + function execute($request, MyService $service = null) { + return $service->execute(); + } + + function executeNamed($request, MyService $service) { + return $service->execute(); + } + + function executeFoo($request, $options = "foo") { + return $options; + } +} + +$context = sfContext::getInstance(array( + 'routing' => 'sfNoRouting', + 'request' => 'sfWebRequest', +)); + +$context->getServiceContainer()->setService("MyService", new MyService()); + +$component = new myComponent($context, 'module', 'action'); + + +$resolver = new sfParameterResolver($context->getServiceContainer()); +$resolver + ->setRequest($context->getRequest()) + ->setComponent($component); + + +$t->diag('execute()'); +$t->is($resolver->execute(), "success", 'without arguments executes default ->execute() method and resolves required dependencies'); +$t->is($resolver->execute('executeNamed'), "success", 'can execute an arbitrary method and resolves required dependencies'); +$t->is($resolver->execute('executeFoo'), "foo", 'uses default value if no type hint is present'); diff --git a/test/unit/sfContextMock.class.php b/test/unit/sfContextMock.class.php index 453567bad..5fceb92e3 100644 --- a/test/unit/sfContextMock.class.php +++ b/test/unit/sfContextMock.class.php @@ -3,7 +3,7 @@ /* * This file is part of the symfony package. * (c) 2004-2006 Fabien Potencier - * + * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ @@ -35,6 +35,7 @@ static public function getInstance($factories = array(), $force = false) self::$instance->storage = new sfSessionTestStorage(array('session_path' => self::$instance->sessionPath)); self::$instance->dispatcher = new sfEventDispatcher(); + self::$instance->serviceContainer = new sfServiceContainer(); foreach ($factories as $type => $class) { @@ -60,6 +61,16 @@ public function getEventDispatcher() return self::$instance->dispatcher; } + public function getServiceContainer() + { + return self::$instance->serviceContainer; + } + + public function getService($service_id) + { + return $this->getServiceContainer()->getService($service_id); + } + public function getModuleName() { return 'module';