Symfony StopWatch events not appearing in profiler timeline - symfony

I'm trying to get additional timing information into the Symfony Profiler Timeline, but I can't get anything to appear. According to the documentation I've read, it should be as simple as the following example, but this doesn't cause any additional info to appear on the timeline.
Do I need to somehow make the profiler aware of the events I'm starting and stopping?
use Symfony\Component\Stopwatch\Stopwatch;
class DefaultController extends Controller
{
public function testAction()
{
$stopwatch = new Stopwatch();
$stopwatch->start('testController');
usleep(1000000);
$response = new Response(
'<body>Hi</body>',
Response::HTTP_OK,
array('content-type' => 'text/html')
);
$event = $stopwatch->stop('testController');
return $response;
}
}

Symfony's profiler can't scan to code for all stopwatch instances and put that into the timeline. You have to use the preconfigured stopwatch provided by the profiler instead:
public function testAction()
{
$stopwatch = $this->get('debug.stopwatch');
$stopwatch->start('testController');
// ...
$event = $stopwatch->stop('testController');
return $response;
}
However, your controller is already on the timeline...

You should inject the stopwacth as a service in your constructor or a specific function, becasue after Symfony 3.4: Services are private by default.
testAction(\Symfony\Component\Stopwatch\Stopwatch $stopwatch) {
$stopwatch->start('testController');
// ...
$event = $stopwatch->stop('testController');
}

Related

Symfony router call controller

I'm writing my own project and want to use some of Symfony components.
I'm implementing Sf Router to my project and I don't understand how to call controller in router.
For ex I have a class:
<?php
namespace App;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\HttpFoundation as Http;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
class AppRouter
{
public function match() {
$context = new RequestContext();
$request = Http\Request::createFromGlobals();
$context->fromRequest($request);
$matcher = new UrlMatcher($this->getRoutes(), $context);
return $matcher->matchRequest($request);
}
private function getRoutes() {
$fileLocator = new FileLocator([__DIR__ . '/../config']);
$loader = new YamlFileLoader($fileLocator);
$routes = $loader->load('routes.yml');
return $routes;
}
}
and I have some defined routes in my routes.yml so if I trying to go to registred route match() method returns to me an array like below:
[
"_controller" => "Controller\\MyController::testAction"
"_route" => "test"
]
So, how now can I call the controller for matched URL? I'm read a documentation but can't understand how should I do this.
This is a much more involved question then it might look.
The easiest approach is to simply explode the _controller string, new the controller and call the action.
// Untested but I think the syntax is correct
$matched = $router->match();
$parts = explode(':',$matched['_controller']);
$controller = new $parts[0]();
$controller->$parts[2]();
Of course there is a great deal of error checking to be added.
The reason I say it can be more complicated is that there is actually a lot you more you can do. Take a look at the HTTP Component. HttpKernel::handle($request) which uses a ControllerResolver class to create a controller as well as an ArgumentResolver to handle many of the controller's arguments. Fun stuff.
Create Your Own Framework is an excellent resource.
And the fairly new Symfony Flex approach gives you a bare bones framework which is worth studying as well.
The HTTP Kernel handles the Route to Controller invokation. You can find the full documentation here
The Controller Resolver and argument resolver will help you to match pretty easily a route to a controller method. This is the easy and robust way in order to get the router up and running on a legacy project without having to invoke the fully-fledge framework.
Think about it once more: our framework is more robust and more flexible than ever and it still has less than 50 lines of code.
require_once __DIR__.'/../vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing;
use Symfony\Component\HttpKernel;
function render_template(Request $request)
{
extract($request->attributes->all(), EXTR_SKIP);
ob_start();
include sprintf(__DIR__.'/../src/pages/%s.php', $_route);
return new Response(ob_get_clean());
}
$request = Request::createFromGlobals();
$routes = include __DIR__.'/../src/app.php';
$context = new Routing\RequestContext();
$context->fromRequest($request);
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);
$controllerResolver = new HttpKernel\Controller\ControllerResolver();
$argumentResolver = new HttpKernel\Controller\ArgumentResolver();
try {
$request->attributes->add($matcher->match($request->getPathInfo()));
$controller = $controllerResolver->getController($request);
$arguments = $argumentResolver->getArguments($request, $controller);
$response = call_user_func_array($controller, $arguments);
} catch (Routing\Exception\ResourceNotFoundException $exception) {
$response = new Response('Not Found', 404);
} catch (Exception $exception) {
$response = new Response('An error occurred', 500);
}
$response->send();
`
My solution for are next:
change a little my routes.yml parameters: no class name contain only slash
use call_user_func_array($this->controller, $this->parameters)

Writing in log file error message from class

How should I write in log file the error or the message I want to from an Entity class?
The idea is like this: I have some items with some properties and also a configuration with some properties. I need to check if the item has the property that also exists in the configuration properties. When I debug my application, at some point I am here:
public function getProperty(idProperty $propertyId)
{
$properties = $this->ItemProperties();
if (isset($properties[$propertyId->getValue()])) {
return $properties[$propertyId->getValue()];
}else{
//here I want to write in the log file that the propertyId is not in the $properties.
}
return null;
}
So how can I achieve that? Thank you.
You can throw Exception and setup Exception Listener, which will write into log.
Inside Entity:
if (isset($properties[$propertyId->getValue()])) {
return $properties[$propertyId->getValue()];
} else {
throw new DomainException('Something is wrong.' . print_r($this, true));
}
In Listener class:
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
$e = $event->getException();
if ($e instanceof DomainException) {
$this->logger->warning('Exception ' . get_class($e) , ['message' => $e->getMessage()]);
$event->setResponse(
new JsonResponse(['error' => $e->getMessage()], 400)
);
services.yml
app.exception_listener:
class: Application\Listeners\ExceptionListener
arguments: ['#domain.logger']
tags:
- { name: kernel.event_listener, event: kernel.exception }
Symfony Events.
You can also inject Logger into your Entity and write to log from there, though it violates Single Responsibility Principle.
UPDATE
DomainException is a simple class, inheriting from \Exception. In this example it only exists to distinguish between your custom exceptions and those that are thrown by PHP or other libraries.
It can also contain additional functionality, for example, accepting two messages in constructor, writing one of them into log file and outputting another for your user.

PHP/Symfony - Parsing object properties from Request

We're building a REST API in Symfony and in many Controllers we're repeating the same code for parsing and settings properties of objects/entities such as this:
$title = $request->request->get('title');
if (isset($title)) {
$titleObj = $solution->getTitle();
$titleObj->setTranslation($language, $title);
$solution->setTitle($titleObj);
}
I'm aware that Symfony forms provide this functionality, however, we've decided in the company that we want to move away from Symfony forms and want to use something simplier and more customisable instead.
Could anybody please provide any ideas or examples of libraries that might achieve property parsing and settings to an object/entity? Thank you!
It seems like a good use case for ParamConverter. Basically it allows you, by using #ParamConverter annotation to convert params which are coming into your controller into anything you want, so you might just create ParamConverter with code which is repeated in many controllers and have it in one place. Then, when using ParamConverter your controller will receive your entity/object as a parameter.
class ExampleParamConverter implements ParamConverterInterface
{
public function apply(Request $request, ParamConverter $configuration)
{
//put any code you want here
$title = $request->request->get('title');
if (isset($title)) {
$titleObj = $solution->getTitle();
$titleObj->setTranslation($language, $title);
$solution->setTitle($titleObj);
}
//now you are setting object which will be injected into controller action
$request->attributes->set($configuration->getName(), $solution);
return true;
}
public function supports(ParamConverter $configuration)
{
return true;
}
}
And in controller:
/**
* #ParamConverter("exampleParamConverter", converter="your_converter")
*/
public function action(Entity $entity)
{
//you have your object available
}

Load custom configuration in a console command using dependency-injection

I have started using Symfony's console components to build various cli tools.
I am currently slapping together such a console app, that has require various configurations, some of which are shared among commands, other configs are unique to the command.
At first I was using a helper class, with a static function call to load a regular configuration array.
Yesterday I refactored this and now load configuration in the config component, along with the treeBuilder mechanism for validation. This is all done in the main console script, not in the "command" classes.
$app = new Application('Console deployment Application', '0.0.1');
/**
* Load configuration
*/
$configDirectories = array(__DIR__.'/config');
$locator = new FileLocator($configDirectories);
$loader = new YamlConfigLoader($locator);
$configValues = $loader->load(file_get_contents($locator->locate("config.yml")));
// process configuration
$processor = new Processor();
$configuration = new Configuration();
try {
$processedConfiguration = $processor->processConfiguration(
$configuration,
$configValues
);
// configuration validated
var_dump($processedConfiguration);
} catch (Exception $e) {
// validation error
echo $e->getMessage() . PHP_EOL;
}
/**
* Load commands
*/
foreach(glob(__DIR__ . '/src/Command/*Command.php') as $FileName) {
$className = "Command\\" . rtrim(basename($FileName), ".php");
$app->addCommands(array(
new $className,
));
}
$app->run();
Currently, the only means to setup the configuration is to setup the code that loads the configuration in a separate class and call this class in in the configure() method of every method.
Maybe there is a more "symfonyish" way of doing this that I missed, I also would like to avoid having the entire framework in codebase, this is meant to be a lightweight console app.
Is there a way to pass the processed configuration to the commands being invoked, using DI or some other method I am not aware of?
Manual Injection
If you wany to keep things light and only have one (the same) configuration object for all commands, you don't even needa DI container. Simply create the commands like this:
...
$app->addCommands(array(
new $className($configuration),
));
Although you have to be aware of the trade-offs, e.g. you will have to have more effort extending this in the future or adjust to changing requirements.
Simple DI Container
You can of course use a DI container, there is a really lightweight container called Twittee, which has less than 140 characters (and thus fits in a tweet). You could simply copy and paste that and add no dependency. In your case this may end up looking similar to:
$c = new Container();
$c->configA = function ($c) {
return new ConfigA();
};
$c->commandA = function($c) {
return new CommandA($c->configA());
}
// ...
You then would need to set that up for all your commands and configurations and then simply for each command:
$app->addCommand($c->commandA());
Interface Injection
You could roll your own simple injection mechanism using interfaces and setter injection. For each dependency you want to inject you will need to define an interface:
interface ConfigAAwareInterface {
public function setConfigA(ConfigA $config);
}
interface ConfigBAwareInterface {
public function setConfigA(ConfigA $config);
}
Any class that needs the dependency can simply implement the interface. As you will mostly repeat the setters, make use of a trait:
trait ConfigAAwareTrait {
private $config;
public function setConfigA(ConfigA $config) { $this->config = $config; }
public function getConfigA() { return $this->config }
}
class MyCommand extends Command implements ConfigAAwareInterface {
use ConfigAAwareTrait;
public function execute($in, $out) {
// access config
$this->getConfigA();
}
}
Now all that is left is to actually instantiate the commands and inject the dependencies. You can use the following simple "injector class":
class Injector {
private $injectors = array();
public function addInjector(callable $injector) {
$this->injectors[] = $injector;
}
public function inject($object) {
// here we'll just call the injector callables
foreach ($this->injectors as $inject) {
$inject($object);
}
return $object;
}
}
$injector = new Injector();
$configA = new ConfigA();
$injector->addInjector(function($object) use ($configA) {
if ($object instanceof ConfigAAwareInterface) {
$object->setConfigA($configA);
}
});
// ... add more injectors
Now to actually construct a command, you can simply call:
$injector->inject(new CommandA());
And the injector will inject dependencies based on the implemented interfaces.
This may at first seem a little complicated, but it is in fact quite helpful at times.
However, if you have multiple objects of the same class that you need to inject (e.g. new Config("path/to/a.cfg") and new Config("path/to/b.cfg")) this might not be an ideal solution, as you can only distinguish by interfaces.
Dependency Injection Library
You can of course also use a whole library and add that as dependency. I have written a list of PHP dependency injection containers in a separate answer.

How to define findOneBy($criteria) function?

I am very new to Symfony2, designing a simple login system. The userclass,
router, controlerclass everything is working fine. I am stuck to
userRepository class.
My controller part is:
public function loginProcessAction(Request $request){
if($request->getMethod() == "POST") {
$username = $request->get('username');
$password = $request->get('password');
$em= $this->getDoctrine()->getEntityManager();
$repository = $em->getRepository("LoginLoginBundle:Student");
$user = $repository->findOneBy(array('username'=>$username,
'password'=>$password));
if($user){
return $this->render('loginSuccess twig page') ;
}
else{
return $this->render('error twig page') ;
}
} else{
return $this->render("login error page");
}
}
How to define findOneBy(username, password) function in reopository class.
This is not the best way to handle authentication when using Symfony2. Take a look at the Security component integrated with Symfony2.
So check How Security Works: Authentication and Authorization part of the security documentation, all you need to implement/configure is Firewalls to handle Authentication and
Access Controls for Authorization.
But ...
Here's an answer to the common question: How to a define findOneBy(parameter1, parameter2) function for a given repository class?
First, map your entity to the appropriate repository as follow,
/*
* #ORM\Entity(repositoryClass="YourNamespace\YourBundle\Entity\yourRepository")
*/
class YourEntity
{
// ...
}
You should then add the mapped repository class and implement a findOneBy(parameter1, parameter2) method.
You can then access this class within your controller as follow,
$em= $this->getDoctrine()->getManager();
$yourEntityInstance = $em->getRepository("yourNamespaceYourBundle:YourEntity")
->findOneBy($parameter1, $parameter2);

Resources