Looking for a design pattern to process or batch process - php

I have the following structure. Until this point each element could be processed one by one.
interface ProcessingStrategyInterface
{
public function process(Element $object): string;
}
class SomeListener
{
/**
* #var array<string, ProcessingStrategyInterface>
*/
protected array $processers;
public function __invoke(DocumentContent $event)
{
/**
* #var array<string, list<Element>> $elements
*/
$elementGroups = $event->getDynamicElements();
foreach ($elementGroups as $type => $elements) {
$processer = $this->processers[$type];
foreach ($elements as $element) {
$result = $processer->process($element);
}
}
// [...]
}
}
Now I have to add a new type of processer which takes the whole list<Element>, and processes them as one, then gives back a string result. For this a new interface could be defined.
interface BatchProcessingStrategyInterface
{
/**
* #param list<Element> $elements
*/
public function batchProcess(array $elements): string;
}
This solution I feel a little bit off:
class SomeListener
{
/**
* #var array<string, ProcessingStrategyInterface|BatchProcessingStrategyInterface>
*/
protected array $processers;
public function __invoke(DocumentContent $event)
{
/**
* #var array<string, list<Element>> $elements
*/
$elementGroups = $event->getDynamicElements();
foreach ($elementGroups as $type => $elements) {
$processer = $this->processers[$type];
if ($processer instanceof BatchProcessingStrategyInterface) {
$result = $processer->batchProcess($elements);
} else {
foreach ($elements as $element) {
$result = $processer->process($element);
}
}
}
// [...]
}
}
I'm wondering if there is a way to solve this in a 100% type-strict way? What I want to achieve is to have single types everywhere by calling them the following way:
class SomeListener
{
/**
* #var array<string, ProcessingStrategyInterface>
*/
protected array $processers;
public function __invoke(DocumentContent $event)
{
/**
* #var array<string, list<Element>> $elements
*/
$elementGroups = $event->getDynamicElements();
foreach ($elementGroups as $type => $elements) {
$processer = $this->processers[$type];
$result = $processer->process($elements);
}
// [...]
}
}

I just came up with a solution with which I can keep to code type-hinted. Basically I just moved the logic a level up into the processor.
interface ProcessingStrategyInterface
{
/**
* #param list<Element> $elements
*/
public function process(array $elements): string;
}
class ProcesserA implements ProcessingStrategyInterface
{
public function process(array $elements): string
{
foreach ($elements as $element) {
$this->processElement($element);
}
}
protected function processElement(Element $element): string
{
// logic
}
}
class BatchProcesserA implements ProcessingStrategyInterface
{
public function process(array $elements): string
{
foreach ($elements as $element) {
$this->processElement($element);
}
}
}

I am not sure, but if I were you, I would make smth like this:
interface ProcessingStrategyInterface {
/**
* #param Element $elements
*/
public function process( Element $elements ): string;
}
interface BatchProcessingStrategyInterface
{
/**
* #param list<Element> $elements
*/
public function process(array $elements): string;
}
class ProcesserA implements ProcessingStrategyInterface {
public function process( Element $element ): string {
// logic
}
}
class BatchProcesserA implements BatchProcessingStrategyInterface {
private ProcessingStrategyInterface $strategy;
public function process( array $elements ): string {
foreach ( $elements as $element ) {
$this->strategy->processElement( $element );
}
}
}

Related

Class property gets null as a value

I have two classes:
DI
namespace engine\DI;
class DI
{
/**
* #var array
*/
private $container = [];
/**
* #param $key
* #param $value
* #return $this
*/
public function set($key, $value)
{
$this->container[$key] = $value;
return $this;
}
/**
* #param $key
* #return mixed
*/
public function get($key)
{
return $this->has($key);
}
public function has($key)
{
return $this->container[$key] ?? null;
}
}
and Cms
namespace engine;
use engine\DI\DI;
class Cms
{
/**
* #var DI
*/
private $di;
public $router;
/**
* #param $di
*/
public function __construct($di)
{
$this->di = $di;
$this->router = $this->di->get('router');
}
/**
* #return void
*/
public function run()
{
//$this->router->add('home', '/', 'HomeController:index');
print_r($this->router);
}
}
As you can see in Cms, I put a $router's value in constructor and for some reason it remains null. The interesting part is when I print_r($this->di->get('router')) it works just fine and returns proper value. Even more, when I call method by this value directly (like $this->di->get('router')->add();) it also works wonders, so the value itself is clearly not null. What could be the reason of this?
Here are two additional files where the value comes from:
Provider:
namespace engine\service\router;
use engine\service\AbstractProvider;
use engine\core\router\Router;
class Provider extends AbstractProvider
{
public string $service_name = 'router';
/**
* #inheritDoc
*/
function init()
{
$router = new Router('cms/');
$this->di->set($this->service_name, $router);
}
Bootstrap:
use engine\Cms;
use engine\DI\DI;
try {
$di = new DI();
$cms = new Cms($di);
$services = require 'config/services.php'; //stores an array with providers
foreach ($services as $key => $service) {
$provider = new $service($di);
$provider->init();
}
$cms->run();
} catch (\ErrorException $e) {
echo $e->getMessage();
}
After adding the additional info, it seems like what Nigel Ren has pointed in the comments is in fact the issue.
You should have it like this:
try {
$di = new DI();
$services = require 'config/services.php'; //stores an array with providers
foreach ($services as $key => $service) {
$provider = new $service($di);
$provider->init();
}
$cms = new Cms($di); // creating the Cms object after di is initialized
$cms->run();
} catch (\ErrorException $e) {
echo $e->getMessage();
}
Have you tried something like that :
public function __construct($di)
{
$this->di = new $di();
$this->router = $this->di->get('router');
}
try {
$di = new DI();
$services = require 'config/services.php';
foreach ($services as $key => $service) {
$provider = new $service($di);
$provider->init();
}
$cms = new Cms($di);
$cms->run();
} catch (\ErrorException $e) {
echo $e->getMessage();
}

PHP RecursiveIteratorIterator empty when passed a RecursiveRegexIterator

A snapshot of Explorer class can be see below -
class Explorer
{
//....
public function getIterator()
{
//$iterator = new FileSearchIterator($this->baseDir, $this->pattern, $this->buildFlag());
if ($this->iterator) {
return $this->iterator;
}
$iterator = new RecursiveDirectoryIterator($this->baseDir, $this->buildFlag());
if ($this->ignoreVcsFiles) {
$this->ignore = array_merge($this->ignore, static::$vcsFiles);
}
if (!empty($this->ignore)) {
$iterator = new ExcludeFilter($iterator, $this->ignore);
}
if ($this->searchFor === static::SEARCH_FILES) {
$iterator = new FilenameRegexFilter($iterator, $this->pattern);
} else {
$iterator = new DirectoryRegexFilter($iterator, $this->pattern);
}
$this->iterator = new RecursiveIteratorIterator($iterator);
return $this->iterator;
}
//......
}
In Tests the given directory contains a single php file. But when filtering for only ****.php*** files using new FilenameRegexFilter(...) and then passing it through RecursiveIteratorIterator results in an empty RecursiveIteratorIterator instance.
You can see the rest of the filter classes (used above) below -
class ExcludeFilter extends \RecursiveFilterIterator
{
protected $iterator;
protected $ignore = [];
/**
* #inheritDoc
*/
public function __construct(\RecursiveIterator $iterator, array $ignore)
{
$this->iterator = $iterator;
$this->ignore = $ignore;
parent::__construct($iterator);
}
public function accept()
{
return !in_array($this->current()->getFilename(), $this->ignore, true);
}
public function getChildren()
{
$children = parent::getChildren();
$children->ignore = $this->ignore;
return $children;
}
}
class FilenameRegexFilter extends RecursiveRegexFilter
{
/**
* #inheritdoc
*/
public function accept()
{
return ($this->current()->isFile() && preg_match($this->regex, $this->current()->getFilename()));
}
/**
* #return \RecursiveRegexIterator
*/
public function getChildren()
{
$children = parent::getChildren();
$children->regex = $this->regex;
return $children;
}
}
abstract class RecursiveRegexFilter extends \RecursiveRegexIterator
{
protected $regex;
protected $iterator;
/**
* RegexFilter constructor.
* #param \RecursiveIterator $iterator
* #param string $regex
*/
public function __construct(\RecursiveIterator $iterator, $regex)
{
$this->iterator = $iterator;
$this->regex = $this->toRegex($regex);
parent::__construct($iterator, $this->regex);
}
/**
* #return \RecursiveRegexIterator
*/
public function getChildren()
{
$children = new static($this->iterator->getChildren(), $this->regex);
return $children;
}
/**
* #param $pattern
* #param array $options
* #return string
*/
public function toRegex($pattern, $options = [])
{
$pattern = str_replace(array('\*', '\?'), array('.*','.'), preg_quote($pattern));
$pattern = '/^' . $pattern . '$/';
if (!empty($options)) {
$pattern .= implode('', $options);
}
return $pattern;
}
}
class DirectoryRegexFilter extends RecursiveRegexFilter
{
public function accept()
{
return ($this->current()->isDir() || preg_match($this->regex, $this->current()->getFilename()));
}
}
I don't get where I'm going wrong. Can someone please point me in the right direction.
EDIT: To simplify my Question largely is how do I use RecursiveDirectoryIterator with RecursiveFilterIterator

Use subclass of a typesafe method parameter [duplicate]

This question already has answers here:
Override method parameter with child interface as a new parameter
(2 answers)
Closed 8 years ago.
I try to use a subclass in a method with typesafe parameter. Don't know if I'm missing something or if the solution is to just don't make the parameter typesafe. What is the cleanest design option for this situation?
Look at following code:
<?php
class MyObject {
public $name;
}
class MySubObject extends MyObject {
public $subProperty;
}
abstract class Renderer {
protected $someProperty;
public abstract function render(MyObject $obj);
}
class RendererA extends Renderer {
public function render(MyObject $obj) {
return $this->someProperty . ': ' . $obj->name;
}
}
// This is the problematic case ------|
class RendererB extends Renderer { // |
public function render(MySubObject $obj) {
return $this->someProperty . ': ' . $obj->name . ' ' .$obj->subProperty;
}
}
/* EOF */
Take a look at this:
https://github.com/symfony/Routing/blob/2.4/Router.php#L225
In this code, the Router will match a request, however it has two ways of doing this depending on the type of Matcher it has an instance of.
That case uses interfaces to differentiate between the object type, however to perform the same technique to your case try this:
class MyObject {
public $name;
}
class MySubObject extends MyObject {
public $subProperty;
}
class Renderer {
public function render(MyObject $obj) {
if($obj instanceof MySubObject) {
return $this->someProperty . ': ' . $obj->name . ' ' .$obj->subProperty;
}
return $this->someProperty . ': ' . $obj->name;
}
}
Assuming that is the only variation of functionality, you don't need an AbstractRenderer.
Since MySubObject extends MyObject it is still type safe to use the extending class as an argument.
EDIT:
This example uses a basic form of the visitor pattern.
The Renderer essentially becomes a parent manager of multiple potential handlers.
The input is handled differently by drivers added at the configuration stage depending of the input's attributes (class in this case, but there are more advanced forms of criteria).
This example can be executed outside of namespaces.
interface TemplateInterface
{
/**
* #return string
*/
public function getName();
}
interface DriverInterface
{
/**
* #param TemplateInterface $template
* #return string
*/
public function doRender(TemplateInterface $template);
}
class Renderer
{
/**
* #var array
*/
protected $drivers;
protected $property = 'Sample Property';
/**
* Constructor
*/
public function __construct()
{
$this->drivers = array();
}
/**
* #param TemplateInterface $template
* #return string
*/
public function render(TemplateInterface $template)
{
$class = get_class($template);
if(false === isset($this->drivers[$class])) {
throw new InvalidArgumentException(sprintf('Renderer Driver supporting class "%s" is not present.', $class));
}
return sprintf('%s: %s', $this->property, $this->drivers[$class]->doRender($template));
}
/**
* #param DriverInterface $driver
* #param $class
* #return $this
*/
public function addDriver(DriverInterface $driver, $class)
{
$this->drivers[$class] = $driver;
return $this;
}
}
class MyTemplate implements TemplateInterface
{
public function getName()
{
return 'my_template';
}
}
class MyOtherTemplate implements TemplateInterface
{
public function getName()
{
return 'my_other_template';
}
public function getOtherProperty()
{
return 'this is another property';
}
}
class MyDriver implements DriverInterface
{
public function doRender(TemplateInterface $template)
{
return $template->getName();
}
}
class MyOtherDriver implements DriverInterface
{
public function doRender(TemplateInterface $template)
{
if(false === $template instanceof MyOtherTemplate) {
throw new InvalidaArgumentException('OtherDriver::doRender argument must be an instance of MyOtherTemplate');
}
return sprintf('%s %s', $template->getName(), $template->getOtherProperty());
}
}
$renderer = new Renderer();
$renderer
->addDriver(new MyDriver(), 'MyTemplate')
->addDriver(new MyOtherDriver(), 'MyOtherTemplate')
;
echo '<pre>';
echo $renderer->render(new MyTemplate()).PHP_EOL;
echo $renderer->render(new MyOtherTemplate());
This is a more advanced example.
This time you don't specify the class of the template, instead you only add the driver, but this driver interface required the supports method be implemented. The renderer will loop through each driver asking them if they support the template.
The first driver to return true will render and return the template.
interface DriverInterface
{
/**
* #param TemplateInterface $template
* #return string
*/
public function doRender(TemplateInterface $template);
/**
* #param TemplateInterface $template
* #return bool
*/
public function supports(TemplateInterface $template);
}
class Renderer
{
/**
* #var array
*/
protected $drivers;
protected $property = 'Sample Property';
/**
* Constructor
*/
public function __construct()
{
$this->drivers = array();
}
/**
* Returns the rendered template, or false if the template was not supported by any driver.
*
* #param TemplateInterface $template
* #return string|false
*/
public function render(TemplateInterface $template)
{
$class = get_class($template);
foreach($this->drivers as $driver) {
if($driver->supports($template)) {
return sprintf('%s: %s', $this->property, $driver->doRender($template));
}
}
return false;
}
/**
* #param DriverInterface $driver
* #param $class
* #return $this
*/
public function addDriver(DriverInterface $driver)
{
$this->drivers[] = $driver;
return $this;
}
}

zf2 doctrine odm collection hydration

Hi everybody Im using doctrine ODM and have trouble with hydrator. I can't make extract on document with embed collection nor reference Class. the extract result for these class give me object and i really need to have them in array for rest module which is consumed by backbone implementation.
Here a example class :
Analyse.php Document
<?php
namespace Application\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Doctrine\Common\Collections\Collection;
/**
* Application\Document\Analyse
*
* #ODM\Document(collection="analyse")
*/
class Analyse extends BaseDocument
{
/**
* #ODM\Id
*/
protected $id;
/**
* #ODM\Field(type="string")
*/
protected $nom;
/**
* #ODM\Field(type="string")
* #ODM\Index
*/
protected $alias;
/**
* #ODM\EmbedMany(targetDocument="ElementsAnalyse")
*/
protected $elements = array();
public function __construct()
{
parent::__construct();
$this->elements = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function setNom($nom)
{
$this->nom = $nom;
return $this;
}
public function getNom()
{
return $this->nom;
}
public function setAlias($alias)
{
$this->alias = $alias;
return $this;
}
public function getAlias()
{
return $this->alias;
}
public function addElements(Collection $elements)
{
foreach ($elements as $element) {
$this->elements->add($element);
}
}
public function removeElements(Collection $elements)
{
foreach ($elements as $item) {
$this->elements->removeElement($item);
}
}
public function getElements()
{
return $this->elements;
}
}
ElementAnalyse.php Collection
<?php
namespace Application\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Doctrine\Common\Collections\Collection;
/**
* Application\Document\Valeurnormales
*
* #ODM\EmbeddedDocument
*
*/
class ElementsAnalyse
{
/**
* #ODM\Field(type="string")
*/
protected $nom;
/**
* #ODM\Field(type="string")
*/
protected $unite;
/**
* #ODM\EmbedMany(targetDocument="ValeurNormales")
*/
protected $valeurnormales = array();
/**
* #ODM\Field(type="string")
*/
protected $type;
public function __construct()
{
$this->valeurnormales = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Set nom
*/
public function setNom($nom)
{
$this->nom = $nom;
return $this;
}
/**
* Get nom
*/
public function getNom()
{
return $this->nom;
}
/**
* Set unite
*/
public function setUnite($unite)
{
$this->unite = $unite;
return $this;
}
/**
* Get unite
*
* #return string
*/
public function getUnite()
{
return $this->unite;
}
/**
* add valeurnormales
*/
public function addValeurnormales(Collection $vn)
{
foreach ($vn as $item) {
$this->valeurnormales->add($item);
}
}
public function removeValeurnormales(Collection $vn)
{
foreach ($vn as $item) {
$this->valeurnormales->removeElement($item);
}
}
/**
* Get valeurnormales
*/
public function getValeurnormales()
{
return $this->valeurnormales;
}
/**
* Set type
*
* #param $type
* #return Analyse
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* Get type
*
* #return Type
*/
public function getType()
{
return $this->type;
}
/**
* toArray function
*/
public function toArray()
{
return get_object_vars($this);
}
/**
* fromArray function
*
*/
public function fromArray(array $array)
{
$objects = $this->toArray();
foreach($array as $item => $value)
{
if(array_key_exists($item, $objects))
{
$this->$item = $value;
}
}
}
}
Here my getList Method
public function getList()
{
$hydrator = new DoctrineHydrator($entityManager, 'Application\Document\Analyse');
$service = $this->getAnalyseService();
$results = $service->findAll();
$data = $hydrator->extract($results);
return new JsonModel($data);
}
And obviously var_dump($data['elements']) return object Collection or proxy class
Can You help me. Anything will be appreciated it been 2 weeks i can't make it work.
Read about Hydrator Strategy out there but i don't knnow how to implement it.
Currently, the Doctrine ODM implementation does not provide recursion for embedded objects and references.
If you use var_dump() on your $hydrator->extract($results), you'll see that all your embeds/references are there in their original object format.
What you can do here is to use Zend\Stdlib\Hydrator\Strategy, and define your own logic for extraction/hydration. Doctrine extends Zend Framework 2's hydrators and strategies.

Fatal error: Declaration of registerContainerConfiguration must be compatible with that of Kernel::registerContainerConfiguration

Do anyone know why this occurs?
as far I can get, the child class method is declared in the same way as parent's.
Thanks!
here is my kernel code:
<?php
require_once __DIR__.'/../src/autoload.php';
use Symfony\Framework\Kernel;
use Symfony\Components\DependencyInjection\Loader\YamlFileLoader as ContainerLoader;
use Symfony\Components\Routing\Loader\YamlFileLoader as RoutingLoader;
use Symfony\Framework\KernelBundle;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\ZendBundle\ZendBundle;
use Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle;
use Symfony\Bundle\DoctrineBundle\DoctrineBundle;
use Symfony\Bundle\DoctrineMigrationsBundle\DoctrineMigrationsBundle;
use Symfony\Bundle\DoctrineMongoDBBundle\DoctrineMongoDBBundle;
use Symfony\Bundle\PropelBundle\PropelBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Application\UfaraBundle\UfaraBundle;
class UfaraKernel extends Kernel {
public function registerRootDir() {
return __DIR__;
}
public function registerBundles() {
$bundles = array(
new KernelBundle(),
new FrameworkBundle(),
new ZendBundle(),
new SwiftmailerBundle(),
new DoctrineBundle(),
//new DoctrineMigrationsBundle(),
//new DoctrineMongoDBBundle(),
//new PropelBundle(),
//new TwigBundle(),
new UfaraBundle(),
);
if ($this->isDebug()) {
}
return $bundles;
}
public function registerBundleDirs() {
$bundles = array(
'Application' => __DIR__.'/../src/Application',
'Bundle' => __DIR__.'/../src/Bundle',
'Symfony\\Framework' => __DIR__.'/../src/vendor/symfony/src/Symfony/Framework',
'Symfony\\Bundle' => __DIR__.'/../src/vendor/symfony/src/Symfony/Bundle',
);
return $bundles;
}
public function registerContainerConfiguration(LoaderInterface $loader) {
return $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
}
public function registerRoutes() {
$loader = new RoutingLoader($this->getBundleDirs());
return $loader->load(__DIR__.'/config/routing.yml');
}
}
here is the parent class code:
<?php
namespace Symfony\Framework;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Resource\FileResource;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Loader\DelegatingLoader;
use Symfony\Component\DependencyInjection\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Framework\ClassCollectionLoader;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier#symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Kernel is the heart of the Symfony system. It manages an environment
* that can host bundles.
*
* #author Fabien Potencier <fabien.potencier#symfony-project.org>
*/
abstract class Kernel implements HttpKernelInterface, \Serializable
{
protected $bundles;
protected $bundleDirs;
protected $container;
protected $rootDir;
protected $environment;
protected $debug;
protected $booted;
protected $name;
protected $startTime;
protected $request;
const VERSION = '2.0.0-DEV';
/**
* Constructor.
*
* #param string $environment The environment
* #param Boolean $debug Whether to enable debugging or not
*/
public function __construct($environment, $debug)
{
$this->environment = $environment;
$this->debug = (Boolean) $debug;
$this->booted = false;
$this->rootDir = realpath($this->registerRootDir());
$this->name = basename($this->rootDir);
if ($this->debug) {
ini_set('display_errors', 1);
error_reporting(-1);
$this->startTime = microtime(true);
} else {
ini_set('display_errors', 0);
}
}
public function __clone()
{
if ($this->debug) {
$this->startTime = microtime(true);
}
$this->booted = false;
$this->container = null;
$this->request = null;
}
abstract public function registerRootDir();
abstract public function registerBundles();
abstract public function registerBundleDirs();
abstract public function registerContainerConfiguration(LoaderInterface $loader);
/**
* Checks whether the current kernel has been booted or not.
*
* #return boolean $booted
*/
public function isBooted()
{
return $this->booted;
}
/**
* Boots the current kernel.
*
* This method boots the bundles, which MUST set
* the DI container.
*
* #throws \LogicException When the Kernel is already booted
*/
public function boot()
{
if (true === $this->booted) {
throw new \LogicException('The kernel is already booted.');
}
if (!$this->isDebug()) {
require_once __DIR__.'/bootstrap.php';
}
$this->bundles = $this->registerBundles();
$this->bundleDirs = $this->registerBundleDirs();
$this->container = $this->initializeContainer();
// load core classes
ClassCollectionLoader::load(
$this->container->getParameter('kernel.compiled_classes'),
$this->container->getParameter('kernel.cache_dir'),
'classes',
$this->container->getParameter('kernel.debug'),
true
);
foreach ($this->bundles as $bundle) {
$bundle->setContainer($this->container);
$bundle->boot();
}
$this->booted = true;
}
/**
* Shutdowns the kernel.
*
* This method is mainly useful when doing functional testing.
*/
public function shutdown()
{
$this->booted = false;
foreach ($this->bundles as $bundle) {
$bundle->shutdown();
$bundle->setContainer(null);
}
$this->container = null;
}
/**
* Reboots the kernel.
*
* This method is mainly useful when doing functional testing.
*
* It is a shortcut for the call to shutdown() and boot().
*/
public function reboot()
{
$this->shutdown();
$this->boot();
}
/**
* Gets the Request instance associated with the master request.
*
* #return Request A Request instance
*/
public function getRequest()
{
return $this->request;
}
/**
* Handles a request to convert it to a response by calling the HttpKernel service.
*
* #param Request $request A Request instance
* #param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
* #param Boolean $raw Whether to catch exceptions or not
*
* #return Response $response A Response instance
*/
public function handle(Request $request = null, $type = HttpKernelInterface::MASTER_REQUEST, $raw = false)
{
if (false === $this->booted) {
$this->boot();
}
if (null === $request) {
$request = $this->container->get('request');
} else {
$this->container->set('request', $request);
}
if (HttpKernelInterface::MASTER_REQUEST === $type) {
$this->request = $request;
}
$response = $this->container->getHttpKernelService()->handle($request, $type, $raw);
$this->container->set('request', $this->request);
return $response;
}
/**
* Gets the directories where bundles can be stored.
*
* #return array An array of directories where bundles can be stored
*/
public function getBundleDirs()
{
return $this->bundleDirs;
}
/**
* Gets the registered bundle names.
*
* #return array An array of registered bundle names
*/
public function getBundles()
{
return $this->bundles;
}
/**
* Checks if a given class name belongs to an active bundle.
*
* #param string $class A class name
*
* #return Boolean true if the class belongs to an active bundle, false otherwise
*/
public function isClassInActiveBundle($class)
{
foreach ($this->bundles as $bundle) {
$bundleClass = get_class($bundle);
if (0 === strpos($class, substr($bundleClass, 0, strrpos($bundleClass, '\\')))) {
return true;
}
}
return false;
}
/**
* Returns the Bundle name for a given class.
*
* #param string $class A class name
*
* #return string The Bundle name or null if the class does not belongs to a bundle
*/
public function getBundleForClass($class)
{
$namespace = substr($class, 0, strrpos($class, '\\'));
foreach (array_keys($this->getBundleDirs()) as $prefix) {
if (0 === $pos = strpos($namespace, $prefix)) {
return substr($namespace, strlen($prefix) + 1, strpos($class, 'Bundle\\') + 7);
}
}
}
public function getName()
{
return $this->name;
}
public function getSafeName()
{
return preg_replace('/[^a-zA-Z0-9_]+/', '', $this->name);
}
public function getEnvironment()
{
return $this->environment;
}
public function isDebug()
{
return $this->debug;
}
public function getRootDir()
{
return $this->rootDir;
}
public function getContainer()
{
return $this->container;
}
public function getStartTime()
{
return $this->debug ? $this->startTime : -INF;
}
public function getCacheDir()
{
return $this->rootDir.'/cache/'.$this->environment;
}
public function getLogDir()
{
return $this->rootDir.'/logs';
}
protected function initializeContainer()
{
$class = $this->getSafeName().ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer';
$location = $this->getCacheDir().'/'.$class;
$reload = $this->debug ? $this->needsReload($class, $location) : false;
if ($reload || !file_exists($location.'.php')) {
$this->buildContainer($class, $location.'.php');
}
require_once $location.'.php';
$container = new $class();
$container->set('kernel', $this);
return $container;
}
public function getKernelParameters()
{
$bundles = array();
foreach ($this->bundles as $bundle) {
$bundles[] = get_class($bundle);
}
return array_merge(
array(
'kernel.root_dir' => $this->rootDir,
'kernel.environment' => $this->environment,
'kernel.debug' => $this->debug,
'kernel.name' => $this->name,
'kernel.cache_dir' => $this->getCacheDir(),
'kernel.logs_dir' => $this->getLogDir(),
'kernel.bundle_dirs' => $this->bundleDirs,
'kernel.bundles' => $bundles,
'kernel.charset' => 'UTF-8',
'kernel.compiled_classes' => array(),
),
$this->getEnvParameters()
);
}
protected function getEnvParameters()
{
$parameters = array();
foreach ($_SERVER as $key => $value) {
if ('SYMFONY__' === substr($key, 0, 9)) {
$parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value;
}
}
return $parameters;
}
protected function needsReload($class, $location)
{
if (!file_exists($location.'.meta') || !file_exists($location.'.php')) {
return true;
}
$meta = unserialize(file_get_contents($location.'.meta'));
$time = filemtime($location.'.php');
foreach ($meta as $resource) {
if (!$resource->isUptodate($time)) {
return true;
}
}
return false;
}
protected function buildContainer($class, $file)
{
$parameterBag = new ParameterBag($this->getKernelParameters());
$container = new ContainerBuilder($parameterBag);
foreach ($this->bundles as $bundle) {
$bundle->registerExtensions($container);
if ($this->debug) {
$container->addObjectResource($bundle);
}
}
if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) {
$container->merge($cont);
}
$container->freeze();
foreach (array('cache', 'logs') as $name) {
$dir = $container->getParameter(sprintf('kernel.%s_dir', $name));
if (!is_dir($dir)) {
if (false === #mkdir($dir, 0777, true)) {
die(sprintf('Unable to create the %s directory (%s)', $name, dirname($dir)));
}
} elseif (!is_writable($dir)) {
die(sprintf('Unable to write in the %s directory (%s)', $name, $dir));
}
}
// cache the container
$dumper = new PhpDumper($container);
$content = $dumper->dump(array('class' => $class));
if (!$this->debug) {
$content = self::stripComments($content);
}
$this->writeCacheFile($file, $content);
if ($this->debug) {
$container->addObjectResource($this);
// save the resources
$this->writeCacheFile($this->getCacheDir().'/'.$class.'.meta', serialize($container->getResources()));
}
}
protected function getContainerLoader(ContainerInterface $container)
{
$resolver = new LoaderResolver(array(
new XmlFileLoader($container, $this->getBundleDirs()),
new YamlFileLoader($container, $this->getBundleDirs()),
new IniFileLoader($container, $this->getBundleDirs()),
new PhpFileLoader($container, $this->getBundleDirs()),
new ClosureLoader($container),
));
return new DelegatingLoader($resolver);
}
/**
* Removes comments from a PHP source string.
*
* We don't use the PHP php_strip_whitespace() function
* as we want the content to be readable and well-formatted.
*
* #param string $source A PHP string
*
* #return string The PHP string with the comments removed
*/
static public function stripComments($source)
{
if (!function_exists('token_get_all')) {
return $source;
}
$output = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
$output .= $token[1];
}
}
// replace multiple new lines with a single newline
$output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output);
// reformat {} "a la python"
$output = preg_replace(array('/\n\s*\{/', '/\n\s*\}/'), array(' {', ' }'), $output);
return $output;
}
protected function writeCacheFile($file, $content)
{
$tmpFile = tempnam(dirname($file), basename($file));
if (false !== #file_put_contents($tmpFile, $content) && #rename($tmpFile, $file)) {
chmod($file, 0644);
return;
}
throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
}
public function serialize()
{
return serialize(array($this->environment, $this->debug));
}
public function unserialize($data)
{
list($environment, $debug) = unserialize($data);
$this->__construct($environment, $debug);
}
}
Your answer lies in the imported namespaces. In the Kernel's file, there's this use clause:
use Symfony\Component\DependencyInjection\Loader\LoaderInterface;
So that ties LoaderInterface to the fully namespaced class Symfony\Component\DependencyInjection\Loader\LoaderInterface.
Basically making the signature:
public function registerContainerConfiguration(Symfony\Component\DependencyInjection\Loader\LoaderInterface $loader);
In your class, you don't import that namespace. So PHP by default assumes the class is in your namespace (since none of the imported namespaces have that interface name).
So your signature is (since you don't declare a namespace):
public function registerContainerConfiguration(\LoaderInterface $loader);
So to get them to match, simply add the use line to the top of your file:
use Symfony\Component\DependencyInjection\Loader\LoaderInterface;

Categories