Symfony – Validator component with Dependency Injection

If you’re using the validation/validator component (eg. https://symfony.com/doc/current/components/validator.html) outside of the symfony framework and need to access a service within a custom constraint, here’s how to do it;

Create a ContainerConstraintValidatorFactory;

If you’re using the validation/validator component outside of the Symfony framework and need to access a service within a custom constraint (eg. http://symfony.com/doc/current/validation/custom_constraint.html), here’s how to do it;

Create a ContainerConstraintValidatorFactory;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorFactory;
use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;

class ContainerConstraintValidatorFactory extends ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface {
 private $container;
 
 public function __construct(ContainerInterface $container) {
  parent::__construct();
  $this->container = $container;
 }
 
 public function getInstance(Constraint $constraint) {
  if($this->container->has($constraint->validatedBy())) {
   return $this->container->get($constraint->validatedBy());
  }
 
  return parent::getInstance($constraint);
 }
}

From there, when you’re creating your ‘validator’, you’ll need to use your newly created factory;

$validatorBuilder = Validation::createValidatorBuilder();
$validatorBuilder->setConstraintValidatorFactory(
   new ContainerConstraintValidatorFactory($container)
);
$validator = $validatorBuilder->getValidator();

This will use your container to check if the constraint-validator class is registered … if so, it’ll be used, otherwise it’ll behave as normal.

Reference: https://stackoverflow.com/questions/40601866/how-to-configure-dependencies-on-custom-validator-with-symfony-components

HSTS – HTTP Strict Transport Security

Enable headers module in Apace2;

a2enmod headers

In :80 host-entry;

 

In :443 host-entry;

Header always set Strict-Transport-Security "max-age=31536000; includeSubdomains;"

This means browsers *should* remember that for the next year (31536000 seconds) you’ll have your site accessible via HTTPS

Ref;

  • https://www.globalsign.com/en/blog/what-is-hsts-and-how-do-i-use-it/
  • https://itigloo.com/security/how-to-configure-http-strict-transport-security-hsts-on-apache-nginx/

Clickjacking

To prevent clickjacking, add in the following header as well;

Header always append X-Frame-Options "DENY"
Header always append Content-Security-Policy "frame-ancestors 'none';"

The 2nd line is to cater for older browsers whom don’t support the X-Frame-Options header

Ref;

  • https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet
  • https://www.owasp.org/index.php/Content_Security_Policy_Cheat_Sheet

The resulting :443 conf file will contain;

Header always set Strict-Transport-Security "max-age=31536000; includeSubdomains;"
Header always append X-Frame-Options "DENY"
Header always append Content-Security-Policy "frame-ancestors 'none';"

Symfony: Adding to the translator dynamically

UPDATE: Symfony: Adding messages to the translator dynamically (updated) contains an update to the post below

To add to the translator (eg. from the DB / by user / etc), you need to first create your own translator class or force Symfony to use the base one (otherwise it’ll compile things together and use a read-only one when it comes to accessing your site).

First, in your services.yml file;

translator:
    public: true
    alias: 'Symfony\Bundle\FrameworkBundle\Translation\Translator'

Symfony\Bundle\FrameworkBundle\Translation\Translator:
  class: Symfony\Bundle\FrameworkBundle\Translation\Translator
  arguments:
      - '@service_container'
      - '@translator.selector'
      - '%kernel.default_locale%'

This just tells Symfony to use the base translator, or alternatively you can use your own as follows;

app/config/services.yml

translator:
  public: true
  alias: 'YourNamespace\YourTranslatorClassName'

From there you can add a listener or just need to obtain an instance of the Translator on-load, and add in any resources as required – eg;

your-listener / controller / etc;

use Symfony\Component\Translation\Loader\ArrayLoader

...

$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ["my.word" => "Hello"], $translator->getLocale());

$translator is an instance of “Symfony\Component\Translation\Translator”.

More info;
https://symfony.com/doc/current/components/translation/usage.html

Symfony – Generating absolute urls

Generating absolute urls (including hostname and scheme (http / https) is super useful for including full urls in templates or email content (eg. when you want to pass on an address to someone which is getting your emails!)

How?

In templates;

{{ absolute_url(path('route_name', {...})) }}

In controllers;

use Symfony\Component\Routing\Generator\UrlGeneratorInterface;


$url = $this->generate(
'app_default_index',
['name' => 'Steve'], UrlGeneratorInterface::ABSOLUTE_URL
);

In services;

First import the class name for the url generator, and instance of the generator;

use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/** @var UrlGeneratorInterface */
protected $urlGenerator;

/**
 * Inject URL Generator as a service
 * @param UrlGeneratorInterface $urlGenerator
 * @required
 */
public function setUrlGeneratorService(UrlGeneratorInterface $urlGenerator) {
    $this->urlGenerator = $urlGenerator;
}

Then, inside your method (in the service);

$url = $this->urlGenerator->generate(
'app_default_index',
['name' => 'Steve'], UrlGeneratorInterface::ABSOLUTE_URL
);

Debugging Webpack

This has caused me several days of exciting times… some of the errors Webpack gives can be a little misleading, showing one error when there’s in-fact a totally different underlying issue which will fix everything!

The following shows you everything you need;

<./node_modules/.bin/encore (or your command here> --watch --progress --display-error-details --verbose

You’ll need to replace the start of this command with something relevant to you, depending on the location of your encore / webpack executable.

Webpack Encore & CometD

This drove me nuts for hours;

These dependencies were not found:
* org/cometd in ./node_modules/cometd-jquery/jquery/jquery.cometd.js

The solution … in your webpack.config.js file;

config = Encore.getWebpackConfig();
config.resolve.alias["org/cometd"] = path.resolve(__dirname, "./node_modules/cometd-jquery/org/cometd");
config.resolve.alias["jquery.cometd"] = path.resolve(__dirname, "./node_modules/cometd-jquery/jquery/jquery.cometd");

// export the final configuration
module.exports = config;

After that you should be all good to go!

If you get an error about the ‘path’ function not existing, then just include the following at the top of the file;

var path = require('path');

Renaming files

To renamed from ‘*.abcd.def’ to ‘*.abc’;

find ./ -depth -name "*.abcd.def" -exec sh -c 'mv "$1" "${1%.abcd.def}.abc"' _ {} \;

When using GIT;

find ./ -depth -name "*.abcd.def" -exec sh -c 'git mv "$1" "${1%.abcd.def}.abc"' _ {} \;

Source; https://askubuntu.com/questions/35922/how-to-change-extension-of-multiple-files-from-command-line