JMSSerializer intersection of grouped properties - php

I have entity as following:
class A
{
/**
* #JMS\Groups({"writable", "other"})
*/
private $varA;
/**
* #JMS\Groups({"writable"})
*/
private $varB;
/**
* #JMS\Groups({"other"})
*/
private $varC;
}
I want to make serializer to generate output for properties that exists in BOTH groups so in simplier words I need an intersection of grouped properties.
$context = SerializationContext::create()->setGroups(['writable' ,'other']);
$serializer->serialize(new A(), 'json', $context);
The code above should output only variable $varA because it has both groups defined.
How to achieve it? The only thing that comes to my mind is to extend GroupExclusionStategy that comes from JMSSerializer but maybe there's better way?

Perhaps a simple solution could be add another group name ("exclusive") to $varA property:
/**
* #JMS\Groups({"writable", "other", "exclusive"})
*/
private $varA;
followed by:
$context = SerializationContext::create()->setGroups('exclusive');
but probably this is just a sample use-case. In the other way, should be created a CustomGroupsExclusionStrategy().
By default GroupsExclusionStrategy() checks whether any property group is included into requested groups:
private function shouldSkipUsingGroups(PropertyMetadata $property, $groups)
{
foreach ($property->groups as $group) {
if (in_array($group, $groups)) {
return false;
}
}
return true;
}
So, to do that you need change this one to:
private function shouldSkipUsingGroups(PropertyMetadata $property, $groups)
{
foreach ($groups as $group) {
if (!in_array($group, $property->groups)) {
return true;
}
}
return false;
}
Thus all requested group must be included into the property groups.
Solution:
https://gist.github.com/yceruto/90b1ac46c8e33d51ec21079725949f77
I leave here an implementation by using a flag "strict" strategy:
$context = SerializationContext::create();
$context->addExclusionStrategy(
new CustomGroupsExclusionStrategy(['writable', 'other'], true)
);
$json = $this->get('serializer')->serialize(new A(), 'json', $context);
Output:
{"varA":"foo"}

I've dag into the code of jms and I've found that setGroups uses GroupsExclusionStrategy but there are also different strategies and ExclusionStrategyInterface. So I've implemented this interface into my own
<?php
namespace AppBundle\Jms\Serializer;
use JMS\Serializer\Context;
use JMS\Serializer\Exclusion\ExclusionStrategyInterface;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\PropertyMetadata;
/**
* Class IntersectGroupsExclusionStrategy
* #package AppBundle\Jms
*/
class IntersectGroupsExclusionStrategy implements ExclusionStrategyInterface
{
/**
* #var array
*/
private $groups;
/**
* IntersectGroupsExclusionStrategy constructor.
* #param array $groups
*/
public function __construct(array $groups)
{
$this->setGroups($groups);
}
/**
* {#inheritDoc}
*/
public function shouldSkipProperty(PropertyMetadata $property, Context $navigatorContext)
{
if (is_array($this->groups) && is_array($property->groups)) {
return !(!empty($this->groups) && array_intersect($this->groups, $property->groups) === $this->groups);
}
return false;
}
/**
* Whether the class should be skipped.
*
* #param ClassMetadata $metadata
*
* #return boolean
*/
public function shouldSkipClass(ClassMetadata $metadata, Context $context)
{
return false;
}
/**
* #param array $groups
* #return $this
*/
public function setGroups(array $groups)
{
$this->groups = $groups;
return $this;
}
}
When serializing instead of using setGroups I've used
$intersectExclusionStrategy = new IntersectGroupsExclusionStrategy($groups);
$serializationContext = SerializationContext::create();
$serializationContext->addExclusionStrategy($intersectExclusionStrategy);
Where $groups held values ['writable' ,'other'].
It worked pretty well.
I've also created test for it if someone needed.
<?php
use AppBundle\Jms\Serializer\IntersectGroupsExclusionStrategy;
class IntersectGroupsExclusionStrategyTest extends PHPUnit_Framework_TestCase
{
public function testShouldSkipPropertyGroups()
{
$intersectExclusionStrategy = new IntersectGroupsExclusionStrategy(['group_a', 'group_b']);
$propertyMetaData = $this->getMock('JMS\Serializer\Metadata\PropertyMetadata', [], [], '', false);
$context = $this->getMock('JMS\Serializer\Context', [], [], '', false);
$propertyMetaData->groups = ['group_a', 'group_b', 'group_c'];
$this->assertNotTrue($intersectExclusionStrategy->shouldSkipProperty($propertyMetaData, $context));
$propertyMetaData->groups = ['group_a', 'group_b'];
$this->assertNotTrue($intersectExclusionStrategy->shouldSkipProperty($propertyMetaData, $context));
}
public function testShouldNotSkipPropertyGroups()
{
$intersectExclusionStrategy = new IntersectGroupsExclusionStrategy(['group_a', 'group_b']);
$propertyMetaData = $this->getMock('JMS\Serializer\Metadata\PropertyMetadata', [], [], '', false);
$context = $this->getMock('JMS\Serializer\Context', [], [], '', false);
$propertyMetaData->groups = ['group_a', 'group_c'];
$this->assertTrue($intersectExclusionStrategy->shouldSkipProperty($propertyMetaData, $context));
$propertyMetaData->groups = ['group_d', 'group_e'];
$this->assertTrue($intersectExclusionStrategy->shouldSkipProperty($propertyMetaData, $context));
$intersectExclusionStrategy = new IntersectGroupsExclusionStrategy([]);
$this->assertTrue($intersectExclusionStrategy->shouldSkipProperty($propertyMetaData, $context));
}
public function testShouldSkipClassReturnsFalse()
{
$intersectExclusionStrategy = new IntersectGroupsExclusionStrategy(['group_a', 'group_b']);
$classMetaData = $this->getMock('JMS\Serializer\Metadata\ClassMetadata', [], [], '', false);
$context = $this->getMock('JMS\Serializer\Context', [], [], '', false);
$this->assertFalse($intersectExclusionStrategy->shouldSkipClass($classMetaData, $context));
}
}

Related

Mock Non-existent Interface in PHP

I am new to PHP unit testing and I need some clarity on how can we mock non-existent Interface. Basically, In my project, I want to test one protected method which is expecting two arguments one is Interface and another is a string.
I want to understand how can I pass Interface as an argument in a method. I tried to Mock the Interface but it's not working for me.
This is my main class
namespace App\Models;
class VerifyNs {
protected function NsCheck(SomeInterface $url, string $domain) {
$answer= dns_get_record($domain, DNS_NS);
if (!empty($answer[0]['target']), ('example.com' == substr($answer[0]['target'], -11)) {
$result = 'NS verification for #url passed., ['#url ' => $url ->getName()]';
return $result
}
return 'failed;
}
}
Here is the test case
class VerifyNsTest extends \PHPUnit\Framework\TestCase
{
/**
* To call protected method
* #param $object
* #param string $method
* #param array $parameters
* #return mixed
* #throws \Exception
*/
private function callMethod($object, string $method , array $parameters = [])
{
try {
$className = get_class($object);
$reflection = new \ReflectionClass($className);
} catch (\ReflectionException $e) {
throw new \Exception($e->getMessage());
}
$method = $reflection->getMethod($method);
$method->setAccessible(true);
return $method->invokeArgs($object, $parameters);
}
/**
* Tests for verifying NS of the domain.
*
* #dataProvider providerTestNsCheck
*/
public function testCheckNs($site, $url, $expected_result) {
$site_name = $this->getMockBuilder(SomeInterface::class)
->setMethods(['getName'])
->getMock();
$site_name->method('getName')->willReturn($site);
$verifyNs = new \App\Models\VerifyNs();
$this->assertEquals($expected_result, $this->callMethod($verifyNs, 'NsCheck', [ $site_name, $url]));
}
/**
* Data provider for testCheckNs().
*
*/
public function providerTestNsCheck() {
return [
[
'mysite',
'example.com',
'NS verification for mysite passed.',
], [
'google',
'google.com',
'failed.'
]
],
];
}
}
After running this, it's giving me an error
TypeError: Argument 1 passed to App\Models\VerifyNs::NsCheck() must be an instance of App\Models\SomeInterface, instance of Mock_SomeInterface_1ee04960 given
Can someone please help here? Is it possible to mock non_existent Interface and pass it as an argument to the method?
I was able to resolve this issue using prophesize
$site = $this->prophesize(SomeInterface::class);
$object =$site->reveal()
then passing $object in the callMethod.

How do I pass the controller by other means than the url in PHP MVC

I realize this may be a dumb question, but I am struggling with a custom MVC project I started in order to learn PHP and I was not able to find what I was looking for anywhere else.
My question is, how do I pass the controller to my router, in a way that does not use the url. How would I make it so that the href in my link tags only provide the category and id and yet make it work. At the moment my urls look like this:
website/controller/method/args
ex: website/articles/post/13
I want the url to be like:
website/category/id-or-slug-from-title
I would really appreciate the help, since it's been bugging me for a week now.
Thanks in advance.
Well, it would be hard to describe possible steps in comment section, so I will add an answer.
The following steps will be exemplary and they do not describe the whole process. I will omit some details
Assuming that you're trying to implement MVC, you should have some kind of bootstrap.php (or something like that)
So, let's create our router class
/**
* Current method
* #var string
*/
protected $method;
/**
* Current args
* #var unknown
*/
protected $args = array();
private static $instance;
/**
* That's how we retrieve class instance
* #return app_router_http
*/
public static function getInstance()
{
if (!isset(static::$instance))
{
static::$instance = new self;
}
return static::$instance;
}
private function __clone() {}
/**
* #throws app_exception
*/
private function __construct()
{
/* парсим текущий урл */
$url = parse_url (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : "/", PHP_URL_PATH);
$url = preg_replace("/\\/{2,}/i", "/", $url);
/* Let's retrieve static route (for example - /category/id-or-slug-from-title) */
if ($_route = $this->getStaticRoute($url))
{
if ($_route->hasErrors())
{
throw new app_exception($_route->getErrors(";"));
}
$this
->setController($_route->getController())
->setMethod($_route->getMethod())
->setArgs($_route->getArgs() ? $_route->getArgs() : null)
;
return;
}
/* Let's retrive dynamic route, because we didn't fing static */
if ($_route = $this->getDynamicRoute($url))
{
if ($_route->hasErrors())
{
throw new app_exception($_route->getErrors(";"));
}
$this
->setController($_route->getController())
->setMethod($_route->getMethod())
->setArgs($_route->getArgs() ? $_route->getArgs() : null);
return;
}
throw new app_exception("Can't found any route objects", 503);
}
/**
* #param string $controller
* #return Router
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
/**
* #return string
*/
public function getController()
{
return $this->controller;
}
/**
* #param string $method
* #return Router
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* #return string
*/
public function getMethod()
{
return $this->method;
}
/**
* #param array $args
* #return Router
*/
public function setArgs(array $args = null)
{
if (isset($args))
{
$this->args = $args;
}
return $this;
}
/**
* #return mixed
*/
public function getArgs()
{
return $this->args;
}
/**
* #param string $route
* #param string $controller
* #param string $method
* #param string $objectId
* #return Route|NULL
*/
public function getStaticRoute($route = null, $controller = null, $method = null, $objectId = null)
{
$db = new DB(); //Some class for db connections
if (isset($route) && !isset($controller) && !isset($method) && !isset($objectId))
{
$selector = "SELECT * FROM `routes` WHERE `route` = '".$db->escape($route)."'";
}
if (!isset($route) && isset($controller) && isset($method) && isset($objectId))
{
$selector = "SELECT * FROM `routes` WHERE `controller` = '".$db->escape($controller)."' && `method` = '".$db->escape($method)."' && `objectId` = '".$db->escape($objectId)."'";
}
if (!isset($selector))
{
throw new app_exception(get_class($this)."::getStaticRoute incorrect params", 503);
}
if ($db->query($selector)->rows())
{
$row = $db->fetch();
$object = new Router();
$object->setAttributes($row);
if (!$object->hasErrors())
{
$object->setController("{$object->getController()}");//Here we are setting our awesome controller
if (!$this->isControllerExist($object->getController()))
{
return $object->addError("Controller {$object->getController()} missing (static route: #{$object->getId()})");
}
if (!$this->isMethodExist($object->getController(), $object->getMethod()))
{
return $object->addError("Method {$object->getMethod()} missing (controller: {$object->getController()}) (static route: #{$object->getId()})");
}
}
return $object;
}
return null;
}
/**
* #param string $path
* #return Router
*/
public function getDynamicRoute($path)
{
$object = new Router;
//Here we will assume that our url looks like this /controller/method/args
/* Url fragments */
$fragments = explode("/", substr($path, 1));
//Removing trailing slash
if (!$fragments[sizeof($fragments)-1])
{
unset($fragments[sizeof($fragments)-1]);
}
if (!isset($fragments[0]))
{
$fragments[0] = APP_ROUTE_DEFAULT_CONTROLLER; //Some kind of magic constant
}
$controller = $fragments[0];
if(!class_exists($controller)
{
throw new Exception("Ooops, controller not found", 404);
}
array_shift($fragments); //We don't need controller element anymore
for ($i = 0; $i <= 1; $i++)
{
if ($i == 0)
{
$method = APP_ROUTE_DEFAULT_METHOD;//We also need to handle urls like /news. For example, each controllers has default method index()
}
else
{
$method = $fragments[0];
}
if ($this->isControllerExist($controller) && $this->isMethodExist($controller, $method))
{
return
$object
->setController($controller)
->setMethod($method);
}
}
return $object->addError("Can't route to <strong>".implode("/", $fragments)."</strong> (dynamic route for module <strong>{$module}</strong>)");
}
/**
* #param string $controller
* #return boolean
*/
public function isControllerExist($controller = null)
{
if (!isset($controller))
{
$controller = $this->getController();
}
return class_exists($controller);
}
/**
* #param string $controller
* #param string $method
* #return boolean
*/
public function isMethodExist($controller = null, $method = null)
{
if (!isset($controller))
{
$controller = $this->getController();
}
if (!isset($method))
{
$method = $this->getMethod();
}
return method_exists($controller, $method);
}
public function run()
{
$_str = $this->getController();
$controller = new $_str;
$return = call_user_func_array(array($controller, $this->getMethod()), $this->getArgs());
return $return;
}
}
So, in bootstrap.php you just need to need to make a call Router::getInstance()->run()
Static route will try to pass your params
For dynamic routes, you always can read args from $_REQUEST
P.S. To be true, this is a cutted examaple made from my old project

Export PHP interface to Typescript interface, or vice versa?

I'm experimenting with Typescript, and at my current contract I code backend in PHP.
In a couple of projects I've written Typescript interfaces for the sort of AJAX responses my back end code gives so that the frontend developer (sometimes also me, sometimes someone else) knows what to expect and gets type checking and so on.
After writing a few such back end services it seems like the interface and related classes for the responses should exist on the PHP side too. And that makes me think that it'd be nice if I could write them in just one of the two languages and run some build-time tool (I'd invoke it with a gulp task, before the Typescript compiler runs) to export these interfaces to the other language.
Does such a thing exist? Is it possible? Practical?
(I realize PHP is not strongly typed, but if the interfaces were written in PHP there could be some type hinting there such as docstrings which the exporter recognizes and carries over to Typescript.)
You can use amazing nikic/PHP-Parser to create a tool for converting selected PHP classes (those with #TypeScriptMe string in phpDoc) to TypeScript interfaces quite easily. The following script is really simple one but I think you can expand it and you can generate TypeScript interfaces automatically and possibly track changes through git.
Example
For this input:
<?php
/**
* #TypeScriptMe
*/
class Person
{
/**
* #var string
*/
public $name;
/**
* #var int
*/
public $age;
/**
* #var \stdClass
*/
public $mixed;
/**
* #var string
*/
private $propertyIsPrivateItWontShow;
}
class IgnoreMe {
public function test() {
}
}
you'll get:
interface Person {
name: string,
age: number,
mixed: any
}
Source codes
index.php:
<?php
namespace TypeScript {
class Property_
{
/** #var string */
public $name;
/** #var string */
public $type;
public function __construct($name, $type = "any")
{
$this->name = $name;
$this->type = $type;
}
public function __toString()
{
return "{$this->name}: {$this->type}";
}
}
class Interface_
{
/** #var string */
public $name;
/** #var Property_[] */
public $properties = [];
public function __construct($name)
{
$this->name = $name;
}
public function __toString()
{
$result = "interface {$this->name} {\n";
$result .= implode(",\n", array_map(function ($p) { return " " . (string)$p;}, $this->properties));
$result .= "\n}";
return $result;
}
}
}
namespace MyParser {
ini_set('display_errors', 1);
require __DIR__ . "/vendor/autoload.php";
use PhpParser;
use PhpParser\Node;
use TypeScript;
class Visitor extends PhpParser\NodeVisitorAbstract
{
private $isActive = false;
/** #var TypeScript/Interface_[] */
private $output = [];
/** #var TypeScript\Interface_ */
private $currentInterface;
public function enterNode(Node $node)
{
if ($node instanceof PhpParser\Node\Stmt\Class_) {
/** #var PhpParser\Node\Stmt\Class_ $class */
$class = $node;
// If there is "#TypeScriptMe" in the class phpDoc, then ...
if ($class->getDocComment() && strpos($class->getDocComment()->getText(), "#TypeScriptMe") !== false) {
$this->isActive = true;
$this->output[] = $this->currentInterface = new TypeScript\Interface_($class->name);
}
}
if ($this->isActive) {
if ($node instanceof PhpParser\Node\Stmt\Property) {
/** #var PhpParser\Node\Stmt\Property $property */
$property = $node;
if ($property->isPublic()) {
$type = $this->parsePhpDocForProperty($property->getDocComment());
$this->currentInterface->properties[] = new TypeScript\Property_($property->props[0]->name, $type);
}
}
}
}
public function leaveNode(Node $node)
{
if ($node instanceof PhpParser\Node\Stmt\Class_) {
$this->isActive = false;
}
}
/**
* #param \PhpParser\Comment|null $phpDoc
*/
private function parsePhpDocForProperty($phpDoc)
{
$result = "any";
if ($phpDoc !== null) {
if (preg_match('/#var[ \t]+([a-z0-9]+)/i', $phpDoc->getText(), $matches)) {
$t = trim(strtolower($matches[1]));
if ($t === "int") {
$result = "number";
}
elseif ($t === "string") {
$result = "string";
}
}
}
return $result;
}
public function getOutput()
{
return implode("\n\n", array_map(function ($i) { return (string)$i;}, $this->output));
}
}
### Start of the main part
$parser = new PhpParser\Parser(new PhpParser\Lexer\Emulative);
$traverser = new PhpParser\NodeTraverser;
$visitor = new Visitor;
$traverser->addVisitor($visitor);
try {
// #todo Get files from a folder recursively
//$code = file_get_contents($fileName);
$code = <<<'EOD'
<?php
/**
* #TypeScriptMe
*/
class Person
{
/**
* #var string
*/
public $name;
/**
* #var int
*/
public $age;
/**
* #var \stdClass
*/
public $mixed;
/**
* #var string
*/
private $propertyIsPrivateItWontShow;
}
class IgnoreMe {
public function test() {
}
}
EOD;
// parse
$stmts = $parser->parse($code);
// traverse
$stmts = $traverser->traverse($stmts);
echo "<pre><code>" . $visitor->getOutput() . "</code></pre>";
} catch (PhpParser\Error $e) {
echo 'Parse Error: ', $e->getMessage();
}
}
composer.json
{
"name": "experiment/experiment",
"description": "...",
"homepage": "http://example.com",
"type": "project",
"license": ["Unlicense"],
"authors": [
{
"name": "MrX",
"homepage": "http://example.com"
}
],
"require": {
"php": ">= 5.4.0",
"nikic/php-parser": "^1.4"
},
"minimum-stability": "stable"
}
you can take a look at TypeSchema where you can generate a JSON format based on your PHP model and convert those JSON format back to different languages i.e. TypeScript, Java, etc. which could solve the problem which you described.
A bit late, but in case you use Symfony as a framework to your project, you can use https://github.com/snakedove/php-to-typescript-converter. It adds a command, e.g. "ts-create-all" to your console, and lets you transform all POPOs, e.g. DTOs, of one folder, to TypeScript interfaces. Works one-way only. It has a special option to translate iterables to arrays of a given Type, which might be useful in some cases.

Reading a value from application.ini

I have a value that's defined in application.ini
conditions.time= 50
How can I read it in an zend action the zend way?
You can use Zend_Config_Ini
$config = new Zend_Config_Ini('my/ini/file.ini');
echo $config->conditions->time; // 50
Here is my approach, which you can use anywhere in the application:
class My_Controller_Action_Helper_Options extends Zend_Controller_Action_Helper_Abstract
{
/**
* Options separator delimiterm e.g.
* option.subkey or
* option/subkey
*/
const DELIMITER = '.';
/**
* Retrieve application options from bootstrap
*
* #return array
*/
public function getOptions()
{
$front = $this->getFrontController();
$bootstrap = $front->getParam('bootstrap');
if (null === $bootstrap) {
throw new Exception('Unable to find bootstrap');
}
return $bootstrap->getOptions();
}
/**
* Get array key if exists, otherwise returns null
*
* #param array $values
* #param string $key
* #return mixed
*/
private static function _getValue($values, $key)
{
if (is_array($values) && isset($values[$key])) {
return $values[$key];
}
return null;
}
/**
* Get application option form bootstrap
*
* #example
* $options = Zend_Controller_Action_HelperBroker::getStaticHelper('options')
* ->get('conditions.time', 'defaultvalue');
*
* #param string $section
* #param mixed $default
* #return Zend_Config
*/
public function get($section = null, $default = null)
{
$value = $this->getOptions();
if (null !== $section && is_string($section)) {
if (false === strpos($section, self::DELIMITER)) {
$value = $this->_getValue($value, $section);
} else {
$sections = explode(self::DELIMITER, $section);
foreach ($sections as $section) {
$value = $this->_getValue($value, $section);
if (null === $value) {
break;
}
}
}
}
if (null === $value) {
return $default;
}
return $value;
}
/**
* #param string $section
* #param mixed $default
* #return Zend_Config
*/
public function direct($section = null, $default = null)
{
return $this->get($section, $default);
}
}
The Application's Bootstrap.php has access to the application.ini using $this->getOptions(), you could store the value you want in your registry something like this:
public function _initConditions()
{
$config = $this->getOptions();
if (isset($config['conditions']))
{
$registry = Zend_Registry::getInstance();
$registry->conditions = $config['conditions'];
}
}
You could then access your conditions using the registry, in much the same way that you set them here.
Here is an action helper for that :
class My_Controller_Action_Helper_Config
extends Zend_Controller_Action_Helper_Abstract
{
/**
* #return array
*/
public function direct()
{
$bootstrap = $this->getActionController()->getInvokeArg('bootstrap');
$ns = strtolower(trim($bootstrap->getAppNamespace(), '_'));
return $bootstrap->getOption($ns);
}
}
You have to put your application namespace as a prefix :
; application.ini
My.conditions.time= 50
You can use it in a controller like this :
$config = $this->_helper->config();
$this->view->time = $config['conditions']['time'];
You might be able to use getenv('conditions.time')
http://www.php.net/manual/en/function.getenv.php
PHP has a parse_ini_file() function.

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