diff --git a/app/AppKernel.php b/app/AppKernel.php index e68667c2a..aa8e8d8b2 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -33,7 +33,11 @@ public function registerBundles() $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); - $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); + + if ('dev' === $this->getEnvironment()) { + $bundles[] = new Symfony\Bundle\WebServerBundle\WebServerBundle(); + $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); + } if ('test' === $this->getEnvironment()) { // this bundle makes it easier to work with databases in PHPUnit diff --git a/app/config/services.yml b/app/config/services.yml index 011368775..aa965d60a 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -1,83 +1,115 @@ services: - # First we define some basic services to make these utilities available in - # the entire application - slugger: - class: AppBundle\Utils\Slugger - markdown: - class: AppBundle\Utils\Markdown + # The special "_defaults" section configures the default behaviors for registering + # new services into the Symfony service container. This section has a local scope + # only, which means it only affects the service definitions being registered by + # this file. + # + # The "autowire" section tells Symfony to make its best to autowire new services + # by introspecting their constructor. + # + # The "public" section with the FALSE boolean value forces all new autowired classes + # to be declared as private services. Marking services private limits their scope to + # the Symfony service container itself only. They're not accessible from the outside + # world and thus let the service container optimizes itself when it's being compiled + # and dumped to raw PHP code. + _defaults: + autowire: true + public: false - # These are the Twig extensions that create new filters and functions for - # using them in the templates - app.twig.app_extension: - public: false - class: AppBundle\Twig\AppExtension - arguments: ['@markdown', '%app_locales%'] - tags: - - { name: twig.extension } + # The special "_instanceof" section appends additional wiring configuration + # to any service definitions that match the listed types. + # + # For instance, the first rule adds a special "kernel.event_subscriber" tag + # to any definitions declared in this file whose class is an instance of the + # "Symfony\Component\EventDispatcher\EventSubscriberInterface" class or interface. + _instanceof: + Symfony\Component\EventDispatcher\EventSubscriberInterface: + tags: ['kernel.event_subscriber'] - app.twig.intl_extension: - public: false - class: Twig_Extensions_Extension_Intl - tags: - - { name: twig.extension } + Symfony\Component\Form\FormTypeInterface: + tags: ['form.type'] - # Defining a form type as a service is only required when the form type - # needs to use some other services, such as the entity manager. - # See http://symfony.com/doc/current/best_practices/forms.html - app.form.type.tagsinput: - class: AppBundle\Form\Type\TagsInputType - arguments: ['@doctrine.orm.entity_manager'] - tags: - - { name: form.type } + Symfony\Component\Security\Core\Authorization\VoterInterface: + tags: ['security.voter'] - # Event Listeners are classes that listen to one or more specific events. - # Those events are defined in the tags added to the service definition. - # See http://symfony.com/doc/current/event_dispatcher.html#creating-an-event-listener - app.redirect_to_preferred_locale_listener: - class: AppBundle\EventListener\RedirectToPreferredLocaleListener - arguments: ['@router', '%app_locales%', '%locale%'] - tags: - - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } + Twig_ExtensionInterface: + tags: ['twig.extension'] - app.comment_notification: - class: AppBundle\EventListener\CommentNotificationListener - arguments: ['@mailer', '@router', '@translator', '%app.notifications.email_sender%'] - # The "method" attribute of this tag is optional and defaults to "on + camelCasedEventName" - # If the event is "comment.created" the method executed by default is "onCommentCreated()". - tags: - - { name: kernel.event_listener, event: comment.created, method: onCommentCreated } + # The following settings will be appended to any local service definitions + # whose class matches this type. In this example, it will only apply to + # the "AppBundle\Controller\Admin\BlogController" service discovered by + # the "AppBundle\Controller\" rule below. + AppBundle\Controller\Admin\BlogController: + getters: + getSlugger: '@AppBundle\Utils\Slugger' - # Event subscribers are similar to event listeners but they don't need service tags. - # Instead, the PHP class of the event subscriber includes a method that returns - # the list of events listened by that class. - # See http://symfony.com/doc/current/event_dispatcher.html#creating-an-event-subscriber - app.requirements_subscriber: - class: AppBundle\EventListener\CheckRequirementsSubscriber - arguments: ['@doctrine.orm.entity_manager'] - tags: - - { name: kernel.event_subscriber } + # This section enables to automatically register all classes found in the matching + # file paths and directories as services in the container. File and directory + # matching uses any valid glob pattern to create a white list of paths. + # + # In this example, the classes found in the following directories will be + # automatically registered as services: + # + # * src/AppBundle/EventListener/ + # * src/AppBundle/Form/Type/ + # * src/AppBundle/Security/ + # * src/AppBundle/Twig/ + # * src/AppBundle/Utils/ + # + # The "resource" attribute can accept more specific glob patterns like + # "{EventListener/*Subscriber.php,Form/Type/*Type.php}". + AppBundle\: + # Register all classes in the src/AppBundle directory as services + resource: '../../src/AppBundle/{EventListener,Form/Type,Security,Twig,Utils}' - # To inject the voter into the security layer, you must declare it as a service and tag it with security.voter. - # See http://symfony.com/doc/current/security/voters.html#configuring-the-voter - app.post_voter: - class: AppBundle\Security\PostVoter - public: false - tags: - - { name: security.voter } + # The other section defines a rule to automatically register and autowire the + # controller classes found in the src/AppBundle/Controller/ directory. + # + # By default all services are made private according to the global "_defaults" + # section at the top of this file. + # + # However, in Symfony, controllers must always be declared public in order to + # be lazy instanciated when they're really needed. This is why the inherited + # default "public" attribute is overriden to force the registered controller + # services to be marked public. + AppBundle\Controller\: + resource: '../../src/AppBundle/Controller' + public: true - # Uncomment the following lines to define a service for the Post Doctrine repository. - # It's not mandatory to create these services, but if you use repositories a lot, - # these services simplify your code: + # This third party Twig extension must be manually registered as a service + # because its class doesn't live in any of the previous defined directories. # - # app.post_repository: - # class: Doctrine\ORM\EntityRepository - # factory: ['@doctrine.orm.entity_manager', getRepository] - # arguments: [AppBundle\Entity\Post] + # Thus, its class is not namespaced and cannot be defined in a global rule. + # Indeed, registering new classes whose filename matches a glob pattern as + # services like in the two previous sections only works for namespaced classes. + app.twig.intl_extension: + class: Twig_Extensions_Extension_Intl + + # Some classes cannot be fully autowired because their methods accept either + # some scalar arguments that Symfony cannot guess or a typehinted dependency + # for which the container has at least two registered services matching the + # type. + # + # Both the "AppBundle\Twig\AppExtension" and "AppBundle\EventListener\RedirectToPreferredLocaleSubscriber" + # classes have a "__construct()" method that receives a "$locales" scalar argument + # that Symfony cannot guess. This is why we must manually and explicitly provide + # the wiring of this argument to complete their service definitions. # - # // traditional code inside a controller - # $entityManager = $this->getDoctrine()->getManager(); - # $posts = $entityManager->getRepository('AppBundle:Post')->findAll(); + # To do so, the remaining unwired named arguments must be defined with their + # corresponding values. Here, the "$locales" named argument is configured to + # receive the value of the global "%app_locales%" parameter defined under the + # "parameters" section of the "app/config/config.yml" file. # - # // same code using repository services - # $posts = $this->get('app.post_repository')->findAll(); + # Note that the order in which the named arguments are defined below doesn't + # matter as they're referenced here by their real names in the PHP code. + # Symfony is then smart enough to make the corresponding matching when compiling, + # optimizing and dumping the container as a raw PHP class in the cache directory. + AppBundle\Twig\AppExtension: + $locales: '%app_locales%' + + AppBundle\EventListener\RedirectToPreferredLocaleSubscriber: + $locales: '%app_locales%' + + AppBundle\EventListener\CommentNotificationSubscriber: + $sender: '%app.notifications.email_sender%' diff --git a/composer.json b/composer.json index bb022f08f..662294a95 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "symfony/monolog-bundle" : "^3.0", "symfony/polyfill-apcu" : "^1.0", "symfony/swiftmailer-bundle" : "^2.3", - "symfony/symfony" : "^3.2", + "symfony/symfony" : "^3.3@dev", "twig/extensions" : "^1.3", "twig/twig" : "^1.28 || ^2.0", "white-october/pagerfanta-bundle" : "^1.0" diff --git a/composer.lock b/composer.lock index 77f3be08e..bd78e2797 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "a201a8d92585c751f757f88752195add", + "content-hash": "c4700dff6f642f4368f2f01454b25dc8", "packages": [ { "name": "doctrine/annotations", @@ -1314,6 +1314,55 @@ ], "time": "2016-08-06T20:24:11+00:00" }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, { "name": "psr/log", "version": "1.0.2", @@ -1361,6 +1410,54 @@ ], "time": "2016-10-10T12:19:37+00:00" }, + { + "name": "psr/simple-cache", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/753fa598e8f3b9966c886fe13f370baa45ef0e24", + "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "time": "2017-01-02T13:31:39+00:00" + }, { "name": "sensio/distribution-bundle", "version": "v5.0.18", @@ -1485,16 +1582,16 @@ }, { "name": "sensiolabs/security-checker", - "version": "v4.0.0", + "version": "v4.0.1", "source": { "type": "git", "url": "https://github.com/sensiolabs/security-checker.git", - "reference": "116027b57b568ed61b7b1c80eeb4f6ee9e8c599c" + "reference": "f2ce0035fc512287978510ca1740cd111d60f89f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/116027b57b568ed61b7b1c80eeb4f6ee9e8c599c", - "reference": "116027b57b568ed61b7b1c80eeb4f6ee9e8c599c", + "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/f2ce0035fc512287978510ca1740cd111d60f89f", + "reference": "f2ce0035fc512287978510ca1740cd111d60f89f", "shasum": "" }, "require": { @@ -1525,7 +1622,7 @@ } ], "description": "A security checker for your composer.lock", - "time": "2016-09-23T18:09:57+00:00" + "time": "2017-02-18T17:53:25+00:00" }, { "name": "swiftmailer/swiftmailer", @@ -2039,23 +2136,25 @@ }, { "name": "symfony/symfony", - "version": "v3.2.4", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/symfony/symfony.git", - "reference": "141569be5b33a7cf0d141fb88422649fe11b0c47" + "reference": "5037c2adbd7a58d67ca7e12e3aa740e6881b38f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/symfony/zipball/141569be5b33a7cf0d141fb88422649fe11b0c47", - "reference": "141569be5b33a7cf0d141fb88422649fe11b0c47", + "url": "https://api.github.com/repos/symfony/symfony/zipball/5037c2adbd7a58d67ca7e12e3aa740e6881b38f1", + "reference": "5037c2adbd7a58d67ca7e12e3aa740e6881b38f1", "shasum": "" }, "require": { "doctrine/common": "~2.4", "php": ">=5.5.9", "psr/cache": "~1.0", + "psr/container": "^1.0", "psr/log": "~1.0", + "psr/simple-cache": "^1.0", "symfony/polyfill-intl-icu": "~1.0", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php56": "~1.0", @@ -2065,10 +2164,13 @@ }, "conflict": { "phpdocumentor/reflection-docblock": "<3.0", - "phpdocumentor/type-resolver": "<0.2.0" + "phpdocumentor/type-resolver": "<0.2.0", + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" }, "provide": { - "psr/cache-implementation": "1.0" + "psr/cache-implementation": "1.0", + "psr/container-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" }, "replace": { "symfony/asset": "self.version", @@ -2083,6 +2185,7 @@ "symfony/dependency-injection": "self.version", "symfony/doctrine-bridge": "self.version", "symfony/dom-crawler": "self.version", + "symfony/dotenv": "self.version", "symfony/event-dispatcher": "self.version", "symfony/expression-language": "self.version", "symfony/filesystem": "self.version", @@ -2116,11 +2219,12 @@ "symfony/validator": "self.version", "symfony/var-dumper": "self.version", "symfony/web-profiler-bundle": "self.version", + "symfony/web-server-bundle": "self.version", "symfony/workflow": "self.version", "symfony/yaml": "self.version" }, "require-dev": { - "cache/integration-tests": "dev-master", + "cache/integration-tests": "^0.15.0", "doctrine/cache": "~1.6", "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.4", @@ -2139,7 +2243,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } }, "autoload": { @@ -2178,7 +2282,7 @@ "keywords": [ "framework" ], - "time": "2017-02-17T00:00:43+00:00" + "time": "2017-02-20T00:48:52+00:00" }, { "name": "twig/extensions", @@ -3734,7 +3838,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "symfony/symfony": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/AppBundle/Controller/Admin/BlogController.php b/src/AppBundle/Controller/Admin/BlogController.php index 903a69c76..50c704992 100644 --- a/src/AppBundle/Controller/Admin/BlogController.php +++ b/src/AppBundle/Controller/Admin/BlogController.php @@ -86,7 +86,7 @@ public function newAction(Request $request) // However, we explicitly add it to improve code readability. // See http://symfony.com/doc/current/best_practices/forms.html#handling-form-submits if ($form->isSubmitted() && $form->isValid()) { - $post->setSlug($this->get('slugger')->slugify($post->getTitle())); + $post->setSlug($this->getSlugger()->slugify($post->getTitle())); $entityManager = $this->getDoctrine()->getManager(); $entityManager->persist($post); @@ -145,7 +145,7 @@ public function editAction(Post $post, Request $request) $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - $post->setSlug($this->get('slugger')->slugify($post->getTitle())); + $post->setSlug($this->getSlugger()->slugify($post->getTitle())); $entityManager->flush(); $this->addFlash('success', 'post.updated_successfully'); @@ -189,4 +189,12 @@ public function deleteAction(Request $request, Post $post) return $this->redirectToRoute('admin_post_index'); } + + /** + * Must be protected to allow Symfony DIC to create a proxy. + */ + protected function getSlugger() + { + throw new \BadMethodCallException('No slugger provided.'); + } } diff --git a/src/AppBundle/Controller/SecurityController.php b/src/AppBundle/Controller/SecurityController.php index a7b222694..73f14992b 100644 --- a/src/AppBundle/Controller/SecurityController.php +++ b/src/AppBundle/Controller/SecurityController.php @@ -13,6 +13,7 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; /** * Controller used to manage the application security. @@ -23,18 +24,23 @@ */ class SecurityController extends Controller { + private $helper; + + public function __construct(AuthenticationUtils $helper) + { + $this->helper = $helper; + } + /** * @Route("/login", name="security_login") */ public function loginAction() { - $helper = $this->get('security.authentication_utils'); - return $this->render('security/login.html.twig', [ // last username entered by the user (if any) - 'last_username' => $helper->getLastUsername(), + 'last_username' => $this->helper->getLastUsername(), // last authentication error (if any) - 'error' => $helper->getLastAuthenticationError(), + 'error' => $this->helper->getLastAuthenticationError(), ]); } diff --git a/src/AppBundle/EventListener/CommentNotificationListener.php b/src/AppBundle/EventListener/CommentNotificationSubscriber.php similarity index 91% rename from src/AppBundle/EventListener/CommentNotificationListener.php rename to src/AppBundle/EventListener/CommentNotificationSubscriber.php index 037fccfe3..ade954c78 100644 --- a/src/AppBundle/EventListener/CommentNotificationListener.php +++ b/src/AppBundle/EventListener/CommentNotificationSubscriber.php @@ -12,6 +12,7 @@ namespace AppBundle\EventListener; use AppBundle\Entity\Comment; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Translation\TranslatorInterface; @@ -21,7 +22,7 @@ * * @author Oleg Voronkovich */ -class CommentNotificationListener +class CommentNotificationSubscriber implements EventSubscriberInterface { /** * @var \Swift_Mailer @@ -95,4 +96,11 @@ public function onCommentCreated(GenericEvent $event) // See http://symfony.com/doc/current/email/dev_environment.html#viewing-from-the-web-debug-toolbar $this->mailer->send($message); } + + public static function getSubscribedEvents() + { + return [ + 'comment.created' => 'onCommentCreated', + ]; + } } diff --git a/src/AppBundle/EventListener/RedirectToPreferredLocaleListener.php b/src/AppBundle/EventListener/RedirectToPreferredLocaleSubscriber.php similarity index 75% rename from src/AppBundle/EventListener/RedirectToPreferredLocaleListener.php rename to src/AppBundle/EventListener/RedirectToPreferredLocaleSubscriber.php index cc08bf92b..b7c5c4a4b 100644 --- a/src/AppBundle/EventListener/RedirectToPreferredLocaleListener.php +++ b/src/AppBundle/EventListener/RedirectToPreferredLocaleSubscriber.php @@ -11,8 +11,10 @@ namespace AppBundle\EventListener; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; /** @@ -23,7 +25,7 @@ * * @author Oleg Voronkovich */ -class RedirectToPreferredLocaleListener +class RedirectToPreferredLocaleSubscriber implements EventSubscriberInterface { /** * @var UrlGeneratorInterface @@ -95,4 +97,29 @@ public function onKernelRequest(GetResponseEvent $event) $event->setResponse($response); } } + + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) + * + * @return array The event names to listen to + */ + public static function getSubscribedEvents() + { + return [ + KernelEvents::REQUEST => 'onKernelRequest', + ]; + } } diff --git a/src/AppBundle/Form/Type/DateTimePickerType.php b/src/AppBundle/Form/Type/DateTimePickerType.php index d0d4b3040..f28fe7ffb 100644 --- a/src/AppBundle/Form/Type/DateTimePickerType.php +++ b/src/AppBundle/Form/Type/DateTimePickerType.php @@ -30,9 +30,9 @@ class DateTimePickerType extends AbstractType { private $formatConverter; - public function __construct() + public function __construct(MomentFormatConverter $converter) { - $this->formatConverter = new MomentFormatConverter(); + $this->formatConverter = $converter; } /**