├── tests └── .gitignore ├── src ├── Controller │ ├── .gitignore │ ├── ClientController.php │ ├── ChatController.php │ └── PublisherController.php └── Kernel.php ├── config ├── packages │ ├── dev │ │ ├── routing.yaml │ │ ├── web_profiler.yaml │ │ ├── debug.yaml │ │ ├── easy_log_handler.yaml │ │ └── monolog.yaml │ ├── test │ │ ├── routing.yaml │ │ ├── validator.yaml │ │ ├── framework.yaml │ │ ├── web_profiler.yaml │ │ └── monolog.yaml │ ├── routing.yaml │ ├── sensio_framework_extra.yaml │ ├── twig.yaml │ ├── mercure.yaml │ ├── validator.yaml │ ├── framework.yaml │ ├── cache.yaml │ ├── prod │ │ └── monolog.yaml │ └── security.yaml ├── routes.yaml ├── routes │ ├── annotations.yaml │ └── dev │ │ ├── twig.yaml │ │ └── web_profiler.yaml ├── bundles.php ├── bootstrap.php └── services.yaml ├── .env.test ├── templates ├── base.html.twig ├── client │ └── index.html.twig └── chat │ └── index.html.twig ├── docker ├── www.conf ├── vhost.conf └── Dockerfile ├── .gitignore ├── bin ├── phpunit └── console ├── docker-compose.yml ├── public └── index.php ├── phpunit.xml.dist ├── README.md ├── .env ├── composer.json └── symfony.lock /tests/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Controller/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/packages/dev/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /config/packages/test/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /config/routes.yaml: -------------------------------------------------------------------------------- 1 | #index: 2 | # path: / 3 | # controller: App\Controller\DefaultController::index 4 | -------------------------------------------------------------------------------- /config/routes/annotations.yaml: -------------------------------------------------------------------------------- 1 | controllers: 2 | resource: ../../src/Controller/ 3 | type: annotation 4 | -------------------------------------------------------------------------------- /config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: ~ 4 | utf8: true 5 | -------------------------------------------------------------------------------- /config/packages/sensio_framework_extra.yaml: -------------------------------------------------------------------------------- 1 | sensio_framework_extra: 2 | router: 3 | annotations: false 4 | -------------------------------------------------------------------------------- /config/packages/test/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | not_compromised_password: false 4 | -------------------------------------------------------------------------------- /config/routes/dev/twig.yaml: -------------------------------------------------------------------------------- 1 | _errors: 2 | resource: '@TwigBundle/Resources/config/routing/errors.xml' 3 | prefix: /_error 4 | -------------------------------------------------------------------------------- /config/packages/test/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: true 3 | session: 4 | storage_id: session.storage.mock_file 5 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | # define your env variables for the test env here 2 | KERNEL_CLASS='App\Kernel' 3 | APP_SECRET='$ecretf0rt3st' 4 | SYMFONY_DEPRECATIONS_HELPER=999999 5 | -------------------------------------------------------------------------------- /config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | default_path: '%kernel.project_dir%/templates' 3 | debug: '%kernel.debug%' 4 | strict_variables: '%kernel.debug%' 5 | -------------------------------------------------------------------------------- /config/packages/test/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: false 3 | intercept_redirects: false 4 | 5 | framework: 6 | profiler: { collect: false } 7 | -------------------------------------------------------------------------------- /config/packages/dev/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: true 3 | intercept_redirects: false 4 | 5 | framework: 6 | profiler: { only_exceptions: false } 7 | -------------------------------------------------------------------------------- /config/packages/mercure.yaml: -------------------------------------------------------------------------------- 1 | mercure: 2 | hubs: 3 | default: 4 | url: '%env(MERCURE_PUBLISH_URL)%' 5 | jwt: '%env(MERCURE_JWT_SECRET)%' 6 | -------------------------------------------------------------------------------- /config/packages/test/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 6 | level: debug 7 | channels: ["!event"] 8 | -------------------------------------------------------------------------------- /config/packages/dev/debug.yaml: -------------------------------------------------------------------------------- 1 | debug: 2 | # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. 3 | # See the "server:dump" command to start a new server. 4 | dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" 5 | -------------------------------------------------------------------------------- /config/routes/dev/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler_wdt: 2 | resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' 3 | prefix: /_wdt 4 | 5 | web_profiler_profiler: 6 | resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' 7 | prefix: /_profiler 8 | -------------------------------------------------------------------------------- /config/packages/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | email_validation_mode: html5 4 | 5 | # Enables validator auto-mapping support. 6 | # For instance, basic validation constraints will be inferred from Doctrine's metadata. 7 | #auto_mapping: 8 | # App\Entity\: [] 9 | -------------------------------------------------------------------------------- /templates/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}1e9{% endblock %} 6 | {% block stylesheets %}{% endblock %} 7 | 8 | 9 | {% block body %}{% endblock %} 10 | {% block javascripts %}{% endblock %} 11 | 12 | 13 | -------------------------------------------------------------------------------- /docker/www.conf: -------------------------------------------------------------------------------- 1 | [www] 2 | user = www-data 3 | group = www-data 4 | listen = ${PORT} 5 | listen.owner = www-data 6 | listen.group = www-data 7 | pm = dynamic 8 | pm.max_children = 80 9 | pm.start_servers = 5 10 | pm.min_spare_servers = 5 11 | pm.max_spare_servers = 35 12 | pm.max_requests = 0 13 | catch_workers_output = yes 14 | clear_env = no 15 | request_terminate_timeout = 30s -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | ###> symfony/framework-bundle ### 3 | /.env.local 4 | /.env.local.php 5 | /.env.*.local 6 | /public/bundles/ 7 | /var/ 8 | /vendor/ 9 | ###< symfony/framework-bundle ### 10 | 11 | ###> symfony/phpunit-bridge ### 12 | .phpunit 13 | /phpunit.xml 14 | ###< symfony/phpunit-bridge ### 15 | 16 | ###> symfony/web-server-bundle ### 17 | /.web-server-pid 18 | ###< symfony/web-server-bundle ### 19 | -------------------------------------------------------------------------------- /bin/phpunit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | render('client/index.html.twig'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /config/packages/dev/easy_log_handler.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | EasyCorp\EasyLog\EasyLogHandler: 3 | public: false 4 | arguments: ['%kernel.logs_dir%/%kernel.environment%.log'] 5 | 6 | #// FIXME: How to add this configuration automatically without messing up with the monolog configuration? 7 | #monolog: 8 | # handlers: 9 | # buffered: 10 | # type: buffer 11 | # handler: easylog 12 | # channels: ['!event'] 13 | # level: debug 14 | # easylog: 15 | # type: service 16 | # id: EasyCorp\EasyLog\EasyLogHandler 17 | -------------------------------------------------------------------------------- /config/packages/dev/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 6 | level: debug 7 | channels: ["!event"] 8 | # uncomment to get logging in your browser 9 | # you may have to allow bigger header sizes in your Web server configuration 10 | #firephp: 11 | # type: firephp 12 | # level: info 13 | #chromephp: 14 | # type: chromephp 15 | # level: info 16 | console: 17 | type: console 18 | process_psr_3_messages: false 19 | channels: ["!event", "!doctrine", "!console"] 20 | -------------------------------------------------------------------------------- /src/Controller/ChatController.php: -------------------------------------------------------------------------------- 1 | render('chat/index.html.twig', [ 20 | 'config' => [ 21 | 'topic' => 'chat', 22 | 'publishRoute' => $this->generateUrl('publisher', ['topic' => 'chat']) 23 | ] 24 | ]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /config/packages/cache.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | # Put the unique name of your app here: the prefix seed 4 | # is used to compute stable namespaces for cache keys. 5 | #prefix_seed: your_vendor_name/app_name 6 | 7 | # The app cache caches to the filesystem by default. 8 | # Other options include: 9 | 10 | # Redis 11 | #app: cache.adapter.redis 12 | #default_redis_provider: redis://localhost 13 | 14 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 15 | #app: cache.adapter.apcu 16 | 17 | # Namespaced pools use the above "app" backend by default 18 | #pools: 19 | #my.dedicated.cache: ~ 20 | -------------------------------------------------------------------------------- /config/packages/prod/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: fingers_crossed 5 | action_level: error 6 | handler: nested 7 | excluded_http_codes: [404, 405] 8 | nested: 9 | type: stream 10 | path: "%kernel.logs_dir%/%kernel.environment%.log" 11 | level: debug 12 | console: 13 | type: console 14 | process_psr_3_messages: false 15 | channels: ["!event", "!doctrine"] 16 | deprecation: 17 | type: stream 18 | path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log" 19 | deprecation_filter: 20 | type: filter 21 | handler: deprecation 22 | max_level: info 23 | channels: ["php"] 24 | -------------------------------------------------------------------------------- /src/Controller/PublisherController.php: -------------------------------------------------------------------------------- 1 | getContent())); 24 | return new Response('success'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], 6 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], 7 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 8 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 9 | Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], 10 | Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true], 11 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], 12 | Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true], 13 | Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true], 14 | ]; 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | php: 5 | build: ./docker 6 | env_file: 7 | - ./.env 8 | volumes: 9 | - ./:/var/www/app:rw,cached 10 | # If you develop on Linux, uncomment the following line to use a bind-mounted host directory instead 11 | # - ./:/var/www/app:rw 12 | working_dir: /var/www/app 13 | 14 | nginx: 15 | image: nginx:1.17-alpine 16 | depends_on: 17 | - php 18 | volumes: 19 | - ./public:/var/www/app/public:ro 20 | - ./docker/vhost.conf:/etc/nginx/conf.d/default.conf 21 | ports: 22 | - "8080:80" 23 | 24 | mercure: 25 | image: dunglas/mercure 26 | environment: 27 | - JWT_KEY=myJWTKey 28 | - DEMO=1 29 | - ALLOW_ANONYMOUS=1 30 | - PUBLISH_ALLOWED_ORIGINS=* 31 | - CORS_ALLOWED_ORIGINS=* 32 | - DEBUG=1 33 | ports: 34 | - "9090:80" -------------------------------------------------------------------------------- /config/packages/security.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers 3 | providers: 4 | in_memory: { memory: ~ } 5 | firewalls: 6 | dev: 7 | pattern: ^/(_(profiler|wdt)|css|images|js)/ 8 | security: false 9 | main: 10 | anonymous: true 11 | 12 | # activate different ways to authenticate 13 | # https://symfony.com/doc/current/security.html#firewalls-authentication 14 | 15 | # https://symfony.com/doc/current/security/impersonating_user.html 16 | # switch_user: true 17 | 18 | # Easy way to control access for large sections of your site 19 | # Note: Only the *first* access control that matches will be used 20 | access_control: 21 | # - { path: ^/admin, roles: ROLE_ADMIN } 22 | # - { path: ^/profile, roles: ROLE_USER } 23 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handle($request); 26 | $response->send(); 27 | $kernel->terminate($request, $response); 28 | -------------------------------------------------------------------------------- /docker/vhost.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | root /var/www/app/public; 4 | 5 | location / { 6 | # try to serve file directly, fallback to index.php 7 | try_files $uri /index.php$is_args$args; 8 | } 9 | 10 | location ~ ^/index\.php(/|$) { 11 | fastcgi_pass php:9000; 12 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 13 | include fastcgi_params; 14 | 15 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 16 | fastcgi_param DOCUMENT_ROOT $realpath_root; 17 | # Prevents URIs that include the front controller. This will 404: 18 | # http://domain.tld/index.php/some-path 19 | # Remove the internal directive to allow URIs like this 20 | internal; 21 | } 22 | 23 | # return 404 for all other php files not matching the front controller 24 | # this prevents access to other php files you don't want to be accessible. 25 | location ~ \.php$ { 26 | return 404; 27 | } 28 | 29 | error_log /dev/stdout; 30 | access_log /dev/stdout; 31 | } 32 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | tests 21 | 22 | 23 | 24 | 25 | 26 | src 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /config/bootstrap.php: -------------------------------------------------------------------------------- 1 | =1.2) 9 | if (is_array($env = @include dirname(__DIR__).'/.env.local.php')) { 10 | foreach ($env as $k => $v) { 11 | $_ENV[$k] = $_ENV[$k] ?? (isset($_SERVER[$k]) && 0 !== strpos($k, 'HTTP_') ? $_SERVER[$k] : $v); 12 | } 13 | } elseif (!class_exists(Dotenv::class)) { 14 | throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); 15 | } else { 16 | // load all the .env files 17 | (new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env'); 18 | } 19 | 20 | $_SERVER += $_ENV; 21 | $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; 22 | $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; 23 | $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Symfony Mercure 2 | Example how to get up and running with symfony mercure 3 | 4 | ## Install Symfony 5 | docker run --rm -it -v $PWD:/app composer create-project symfony/website-skeleton symfony_mercure 6 | 7 | docker run --rm -it -v $PWD:/app composer remove doctrine 8 | 9 | docker run --rm -it -v $PWD:/app composer remove mailer 10 | 11 | ## Install the Mercure component 12 | docker run --rm -it -v $PWD:/app composer require mercure 13 | 14 | ## Run the infrastructure 15 | docker-compose up 16 | 17 | ## Test the mercure hub 18 | ``` 19 | curl --request POST \ 20 | --url http://localhost:9090/hub \ 21 | --header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6W10sInB1Ymxpc2giOlsiKiJdfX0.iTVjHoLv9bB-O5RNnTtzOFxIW-YECk2JXZeMekZ4GwA' \ 22 | --header 'content-type: application/x-www-form-urlencoded' \ 23 | --data topic=1e9 \ 24 | --data 'data={ 25 | "headline": "What a nice conf." 26 | }' 27 | ``` 28 | 29 | ## Mercure Demo UI: http://localhost:9090/ 30 | 31 | ## Routes 32 | - Client example: localhost:8080/client 33 | - Chat example: localhost:8080/chat 34 | - Publisher example POST route: localhost:8080/publish/{topic} 35 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # In all environments, the following files are loaded if they exist, 2 | # the later taking precedence over the former: 3 | # 4 | # * .env contains default values for the environment variables needed by the app 5 | # * .env.local uncommitted file with local overrides 6 | # * .env.$APP_ENV committed environment-specific defaults 7 | # * .env.$APP_ENV.local uncommitted environment-specific overrides 8 | # 9 | # Real environment variables win over .env files. 10 | # 11 | # DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. 12 | # 13 | # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). 14 | # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration 15 | 16 | ###> symfony/framework-bundle ### 17 | APP_ENV=dev 18 | APP_SECRET=05c2508615d49af0d6a420e776841552 19 | #TRUSTED_PROXIES=127.0.0.1,127.0.0.2 20 | #TRUSTED_HOSTS='^localhost|example\.com$' 21 | ###< symfony/framework-bundle ### 22 | ###> symfony/mercure-bundle ### 23 | MERCURE_PUBLISH_URL=http://mercure/hub 24 | MERCURE_JWT_SECRET=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6W10sInB1Ymxpc2giOlsiKiJdfX0.iTVjHoLv9bB-O5RNnTtzOFxIW-YECk2JXZeMekZ4GwA 25 | ###< symfony/mercure-bundle ### 26 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], null, true)) { 23 | putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); 24 | } 25 | 26 | if ($input->hasParameterOption('--no-debug', true)) { 27 | putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); 28 | } 29 | 30 | require dirname(__DIR__).'/config/bootstrap.php'; 31 | 32 | if ($_SERVER['APP_DEBUG']) { 33 | umask(0000); 34 | 35 | if (class_exists(Debug::class)) { 36 | Debug::enable(); 37 | } 38 | } 39 | 40 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 41 | $application = new Application($kernel); 42 | $application->run($input); 43 | -------------------------------------------------------------------------------- /config/services.yaml: -------------------------------------------------------------------------------- 1 | # This file is the entry point to configure your own services. 2 | # Files in the packages/ subdirectory configure your dependencies. 3 | 4 | # Put parameters here that don't need to change on each machine where the app is deployed 5 | # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration 6 | parameters: 7 | env(MERCURE_PUBLISH_URL): '' 8 | env(MERCURE_JWT_SECRET): '' 9 | 10 | services: 11 | # default configuration for services in *this* file 12 | _defaults: 13 | autowire: true # Automatically injects dependencies in your services. 14 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. 15 | 16 | # makes classes in src/ available to be used as services 17 | # this creates a service per class whose id is the fully-qualified class name 18 | App\: 19 | resource: '../src/*' 20 | exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' 21 | 22 | # controllers are imported separately to make sure services can be injected 23 | # as action arguments even if you don't extend any base controller class 24 | App\Controller\: 25 | resource: '../src/Controller' 26 | tags: ['controller.service_arguments'] 27 | 28 | # add more service definitions when explicit configuration is needed 29 | # please note that last definitions always *replace* previous ones 30 | -------------------------------------------------------------------------------- /templates/client/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block title %}Hello ClientController!{% endblock %} 4 | 5 | {% block body %} 6 | 10 | 11 |
12 |

Subscriber example

13 |
14 |
15 | {% endblock %} 16 | 17 | {% block javascripts %} 18 | {% endblock %} 46 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.10 2 | 3 | ENV DEBIAN_FRONTEND noninteractive 4 | ENV HOME /root 5 | ENV LC_ALL C.UTF-8 6 | ENV LANG en_US.UTF-8 7 | ENV LANGUAGE en_US.UTF-8 8 | ENV TZ 'Europe/Berlin' 9 | ENV PHPV 7.3 10 | ENV PORT 9000 11 | 12 | RUN apt update \ 13 | && apt install -y software-properties-common \ 14 | && add-apt-repository ppa:ondrej/php \ 15 | && apt update \ 16 | && apt install -y php$PHPV \ 17 | php$PHPV-fpm \ 18 | php$PHPV-curl \ 19 | php$PHPV-dom \ 20 | php$PHPV-gd \ 21 | php$PHPV-mbstring \ 22 | php$PHPV-zip \ 23 | php$PHPV-mysql \ 24 | php$PHPV-apcu \ 25 | php$PHPV-redis \ 26 | php$PHPV-cli 27 | 28 | 29 | RUN echo $TZ > /etc/timezone && \ 30 | apt-get update && apt-get install -y tzdata && \ 31 | rm /etc/localtime && \ 32 | ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ 33 | dpkg-reconfigure -f noninteractive tzdata && \ 34 | apt-get clean 35 | 36 | #fpm settings 37 | RUN sed -i 's|;date.timezone =|date.timezone = "Europe/Berlin"|g' /etc/php/$PHPV/fpm/php.ini \ 38 | && sed -i 's|memory_limit = 128M|memory_limit = 256M|g' /etc/php/$PHPV/fpm/php.ini \ 39 | && sed -i 's|upload_max_filesize = 2M|upload_max_filesize = 8M|g' /etc/php/$PHPV/fpm/php.ini \ 40 | && mkdir -p /run/php 41 | 42 | #cli settings 43 | RUN sed -i 's|;date.timezone =|date.timezone = "Europe/Berlin"|g' /etc/php/$PHPV/cli/php.ini \ 44 | && sed -i 's|memory_limit = 128M|memory_limit = 256M|g' /etc/php/$PHPV/cli/php.ini 45 | 46 | ADD www.conf /etc/php/$PHPV/fpm/pool.d/ 47 | 48 | EXPOSE $PORT 49 | ENTRYPOINT /usr/sbin/php-fpm${PHPV} --nodaemonize --force-stderr -------------------------------------------------------------------------------- /templates/chat/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block title %}Hello ChatController!{% endblock %} 4 | 5 | {% block body %} 6 |

Chat example

7 | 8 |
9 | 10 | 11 | 12 |
13 |
14 | 15 |
16 | No messages 17 |
18 | 19 | {% endblock %} 20 | 21 | {% block javascripts %} 22 | 25 | 58 | {% endblock %} 59 | -------------------------------------------------------------------------------- /src/Kernel.php: -------------------------------------------------------------------------------- 1 | getProjectDir().'/config/bundles.php'; 21 | foreach ($contents as $class => $envs) { 22 | if ($envs[$this->environment] ?? $envs['all'] ?? false) { 23 | yield new $class(); 24 | } 25 | } 26 | } 27 | 28 | public function getProjectDir(): string 29 | { 30 | return \dirname(__DIR__); 31 | } 32 | 33 | protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void 34 | { 35 | $container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php')); 36 | $container->setParameter('container.dumper.inline_class_loader', true); 37 | $confDir = $this->getProjectDir().'/config'; 38 | 39 | $loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob'); 40 | $loader->load($confDir.'/{packages}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); 41 | $loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob'); 42 | $loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob'); 43 | } 44 | 45 | protected function configureRoutes(RouteCollectionBuilder $routes): void 46 | { 47 | $confDir = $this->getProjectDir().'/config'; 48 | 49 | $routes->import($confDir.'/{routes}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob'); 50 | $routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob'); 51 | $routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob'); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "project", 3 | "license": "proprietary", 4 | "require": { 5 | "php": "^7.1.3", 6 | "ext-ctype": "*", 7 | "ext-iconv": "*", 8 | "sensio/framework-extra-bundle": "^5.1", 9 | "symfony/asset": "4.3.*", 10 | "symfony/console": "4.3.*", 11 | "symfony/dotenv": "4.3.*", 12 | "symfony/expression-language": "4.3.*", 13 | "symfony/flex": "^1.3.1", 14 | "symfony/form": "4.3.*", 15 | "symfony/framework-bundle": "4.3.*", 16 | "symfony/http-client": "4.3.*", 17 | "symfony/intl": "4.3.*", 18 | "symfony/mercure-bundle": "^0.1.2", 19 | "symfony/monolog-bundle": "^3.1", 20 | "symfony/process": "4.3.*", 21 | "symfony/security-bundle": "4.3.*", 22 | "symfony/serializer-pack": "*", 23 | "symfony/twig-bundle": "4.3.*", 24 | "symfony/validator": "4.3.*", 25 | "symfony/web-link": "4.3.*", 26 | "symfony/yaml": "4.3.*" 27 | }, 28 | "require-dev": { 29 | "symfony/debug-pack": "*", 30 | "symfony/maker-bundle": "^1.0", 31 | "symfony/profiler-pack": "*", 32 | "symfony/test-pack": "*", 33 | "symfony/web-server-bundle": "4.3.*" 34 | }, 35 | "config": { 36 | "preferred-install": { 37 | "*": "dist" 38 | }, 39 | "sort-packages": true 40 | }, 41 | "autoload": { 42 | "psr-4": { 43 | "App\\": "src/" 44 | } 45 | }, 46 | "autoload-dev": { 47 | "psr-4": { 48 | "App\\Tests\\": "tests/" 49 | } 50 | }, 51 | "replace": { 52 | "paragonie/random_compat": "2.*", 53 | "symfony/polyfill-ctype": "*", 54 | "symfony/polyfill-iconv": "*", 55 | "symfony/polyfill-php71": "*", 56 | "symfony/polyfill-php70": "*", 57 | "symfony/polyfill-php56": "*" 58 | }, 59 | "scripts": { 60 | "auto-scripts": { 61 | "cache:clear": "symfony-cmd", 62 | "assets:install %PUBLIC_DIR%": "symfony-cmd" 63 | }, 64 | "post-install-cmd": [ 65 | "@auto-scripts" 66 | ], 67 | "post-update-cmd": [ 68 | "@auto-scripts" 69 | ] 70 | }, 71 | "conflict": { 72 | "symfony/symfony": "*" 73 | }, 74 | "extra": { 75 | "symfony": { 76 | "allow-contrib": false, 77 | "require": "4.3.*" 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /symfony.lock: -------------------------------------------------------------------------------- 1 | { 2 | "doctrine/annotations": { 3 | "version": "1.0", 4 | "recipe": { 5 | "repo": "github.com/symfony/recipes", 6 | "branch": "master", 7 | "version": "1.0", 8 | "ref": "cb4152ebcadbe620ea2261da1a1c5a9b8cea7672" 9 | }, 10 | "files": [ 11 | "config/routes/annotations.yaml" 12 | ] 13 | }, 14 | "doctrine/cache": { 15 | "version": "v1.8.0" 16 | }, 17 | "doctrine/collections": { 18 | "version": "v1.6.2" 19 | }, 20 | "doctrine/event-manager": { 21 | "version": "v1.0.0" 22 | }, 23 | "doctrine/inflector": { 24 | "version": "v1.3.0" 25 | }, 26 | "doctrine/lexer": { 27 | "version": "1.0.2" 28 | }, 29 | "doctrine/persistence": { 30 | "version": "1.1.1" 31 | }, 32 | "doctrine/reflection": { 33 | "version": "v1.0.0" 34 | }, 35 | "easycorp/easy-log-handler": { 36 | "version": "1.0", 37 | "recipe": { 38 | "repo": "github.com/symfony/recipes", 39 | "branch": "master", 40 | "version": "1.0", 41 | "ref": "70062abc2cd58794d2a90274502f81b55cd9951b" 42 | }, 43 | "files": [ 44 | "config/packages/dev/easy_log_handler.yaml" 45 | ] 46 | }, 47 | "fig/link-util": { 48 | "version": "1.0.0" 49 | }, 50 | "monolog/monolog": { 51 | "version": "1.24.0" 52 | }, 53 | "nikic/php-parser": { 54 | "version": "v4.2.2" 55 | }, 56 | "phpdocumentor/reflection-common": { 57 | "version": "1.0.1" 58 | }, 59 | "phpdocumentor/reflection-docblock": { 60 | "version": "4.3.1" 61 | }, 62 | "phpdocumentor/type-resolver": { 63 | "version": "0.4.0" 64 | }, 65 | "psr/cache": { 66 | "version": "1.0.1" 67 | }, 68 | "psr/container": { 69 | "version": "1.0.0" 70 | }, 71 | "psr/link": { 72 | "version": "1.0.0" 73 | }, 74 | "psr/log": { 75 | "version": "1.1.0" 76 | }, 77 | "sensio/framework-extra-bundle": { 78 | "version": "5.2", 79 | "recipe": { 80 | "repo": "github.com/symfony/recipes", 81 | "branch": "master", 82 | "version": "5.2", 83 | "ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b" 84 | }, 85 | "files": [ 86 | "config/packages/sensio_framework_extra.yaml" 87 | ] 88 | }, 89 | "symfony/asset": { 90 | "version": "v4.3.2" 91 | }, 92 | "symfony/browser-kit": { 93 | "version": "v4.3.2" 94 | }, 95 | "symfony/cache": { 96 | "version": "v4.3.2" 97 | }, 98 | "symfony/cache-contracts": { 99 | "version": "v1.1.5" 100 | }, 101 | "symfony/config": { 102 | "version": "v4.3.2" 103 | }, 104 | "symfony/console": { 105 | "version": "3.3", 106 | "recipe": { 107 | "repo": "github.com/symfony/recipes", 108 | "branch": "master", 109 | "version": "3.3", 110 | "ref": "482d233eb8de91ebd042992077bbd5838858890c" 111 | }, 112 | "files": [ 113 | "bin/console", 114 | "config/bootstrap.php" 115 | ] 116 | }, 117 | "symfony/css-selector": { 118 | "version": "v4.3.2" 119 | }, 120 | "symfony/debug": { 121 | "version": "v4.3.2" 122 | }, 123 | "symfony/debug-bundle": { 124 | "version": "4.1", 125 | "recipe": { 126 | "repo": "github.com/symfony/recipes", 127 | "branch": "master", 128 | "version": "4.1", 129 | "ref": "f8863cbad2f2e58c4b65fa1eac892ab189971bea" 130 | }, 131 | "files": [ 132 | "config/packages/dev/debug.yaml" 133 | ] 134 | }, 135 | "symfony/debug-pack": { 136 | "version": "v1.0.7" 137 | }, 138 | "symfony/dependency-injection": { 139 | "version": "v4.3.2" 140 | }, 141 | "symfony/dom-crawler": { 142 | "version": "v4.3.2" 143 | }, 144 | "symfony/dotenv": { 145 | "version": "v4.3.2" 146 | }, 147 | "symfony/event-dispatcher": { 148 | "version": "v4.3.2" 149 | }, 150 | "symfony/event-dispatcher-contracts": { 151 | "version": "v1.1.5" 152 | }, 153 | "symfony/expression-language": { 154 | "version": "v4.3.2" 155 | }, 156 | "symfony/filesystem": { 157 | "version": "v4.3.2" 158 | }, 159 | "symfony/finder": { 160 | "version": "v4.3.2" 161 | }, 162 | "symfony/flex": { 163 | "version": "1.0", 164 | "recipe": { 165 | "repo": "github.com/symfony/recipes", 166 | "branch": "master", 167 | "version": "1.0", 168 | "ref": "dc3fc2e0334a4137c47cfd5a3ececc601fa61a0b" 169 | }, 170 | "files": [ 171 | ".env" 172 | ] 173 | }, 174 | "symfony/form": { 175 | "version": "v4.3.2" 176 | }, 177 | "symfony/framework-bundle": { 178 | "version": "4.2", 179 | "recipe": { 180 | "repo": "github.com/symfony/recipes", 181 | "branch": "master", 182 | "version": "4.2", 183 | "ref": "0dfae0b1cd8349ae47659b35602f772b4a7e2280" 184 | }, 185 | "files": [ 186 | "config/bootstrap.php", 187 | "config/packages/cache.yaml", 188 | "config/packages/framework.yaml", 189 | "config/packages/test/framework.yaml", 190 | "config/services.yaml", 191 | "public/index.php", 192 | "src/Controller/.gitignore", 193 | "src/Kernel.php" 194 | ] 195 | }, 196 | "symfony/http-client": { 197 | "version": "v4.3.2" 198 | }, 199 | "symfony/http-client-contracts": { 200 | "version": "v1.1.5" 201 | }, 202 | "symfony/http-foundation": { 203 | "version": "v4.3.2" 204 | }, 205 | "symfony/http-kernel": { 206 | "version": "v4.3.2" 207 | }, 208 | "symfony/inflector": { 209 | "version": "v4.3.2" 210 | }, 211 | "symfony/intl": { 212 | "version": "v4.3.2" 213 | }, 214 | "symfony/maker-bundle": { 215 | "version": "1.0", 216 | "recipe": { 217 | "repo": "github.com/symfony/recipes", 218 | "branch": "master", 219 | "version": "1.0", 220 | "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" 221 | } 222 | }, 223 | "symfony/mercure": { 224 | "version": "v0.2.0" 225 | }, 226 | "symfony/mercure-bundle": { 227 | "version": "0.1", 228 | "recipe": { 229 | "repo": "github.com/symfony/recipes", 230 | "branch": "master", 231 | "version": "0.1", 232 | "ref": "c78ab1f56e700004fc5cd675c26b3c1c26be281d" 233 | }, 234 | "files": [ 235 | "config/packages/mercure.yaml" 236 | ] 237 | }, 238 | "symfony/mime": { 239 | "version": "v4.3.2" 240 | }, 241 | "symfony/monolog-bridge": { 242 | "version": "v4.3.2" 243 | }, 244 | "symfony/monolog-bundle": { 245 | "version": "3.3", 246 | "recipe": { 247 | "repo": "github.com/symfony/recipes", 248 | "branch": "master", 249 | "version": "3.3", 250 | "ref": "6240c6d43e8237a32452f057f81816820fd56ab6" 251 | }, 252 | "files": [ 253 | "config/packages/dev/monolog.yaml", 254 | "config/packages/prod/monolog.yaml", 255 | "config/packages/test/monolog.yaml" 256 | ] 257 | }, 258 | "symfony/options-resolver": { 259 | "version": "v4.3.2" 260 | }, 261 | "symfony/phpunit-bridge": { 262 | "version": "4.3", 263 | "recipe": { 264 | "repo": "github.com/symfony/recipes", 265 | "branch": "master", 266 | "version": "4.3", 267 | "ref": "b0582341f1df39aaf3a9a866cdbe49937da35984" 268 | }, 269 | "files": [ 270 | ".env.test", 271 | "bin/phpunit", 272 | "config/bootstrap.php", 273 | "phpunit.xml.dist", 274 | "tests/.gitignore" 275 | ] 276 | }, 277 | "symfony/polyfill-intl-icu": { 278 | "version": "v1.11.0" 279 | }, 280 | "symfony/polyfill-intl-idn": { 281 | "version": "v1.11.0" 282 | }, 283 | "symfony/polyfill-mbstring": { 284 | "version": "v1.11.0" 285 | }, 286 | "symfony/polyfill-php72": { 287 | "version": "v1.11.0" 288 | }, 289 | "symfony/polyfill-php73": { 290 | "version": "v1.11.0" 291 | }, 292 | "symfony/process": { 293 | "version": "v4.3.2" 294 | }, 295 | "symfony/profiler-pack": { 296 | "version": "v1.0.4" 297 | }, 298 | "symfony/property-access": { 299 | "version": "v4.3.2" 300 | }, 301 | "symfony/property-info": { 302 | "version": "v4.3.2" 303 | }, 304 | "symfony/routing": { 305 | "version": "4.2", 306 | "recipe": { 307 | "repo": "github.com/symfony/recipes", 308 | "branch": "master", 309 | "version": "4.2", 310 | "ref": "5374e24d508ba8fd6ba9eb15170255fdb778316a" 311 | }, 312 | "files": [ 313 | "config/packages/dev/routing.yaml", 314 | "config/packages/routing.yaml", 315 | "config/packages/test/routing.yaml", 316 | "config/routes.yaml" 317 | ] 318 | }, 319 | "symfony/security-bundle": { 320 | "version": "3.3", 321 | "recipe": { 322 | "repo": "github.com/symfony/recipes", 323 | "branch": "master", 324 | "version": "3.3", 325 | "ref": "9a2034eca6d83d9cda632014e06995b8d9d9fd09" 326 | }, 327 | "files": [ 328 | "config/packages/security.yaml" 329 | ] 330 | }, 331 | "symfony/security-core": { 332 | "version": "v4.3.2" 333 | }, 334 | "symfony/security-csrf": { 335 | "version": "v4.3.2" 336 | }, 337 | "symfony/security-guard": { 338 | "version": "v4.3.2" 339 | }, 340 | "symfony/security-http": { 341 | "version": "v4.3.2" 342 | }, 343 | "symfony/serializer": { 344 | "version": "v4.3.2" 345 | }, 346 | "symfony/serializer-pack": { 347 | "version": "v1.0.2" 348 | }, 349 | "symfony/service-contracts": { 350 | "version": "v1.1.5" 351 | }, 352 | "symfony/stopwatch": { 353 | "version": "v4.3.2" 354 | }, 355 | "symfony/test-pack": { 356 | "version": "v1.0.6" 357 | }, 358 | "symfony/translation-contracts": { 359 | "version": "v1.1.5" 360 | }, 361 | "symfony/twig-bridge": { 362 | "version": "v4.3.2" 363 | }, 364 | "symfony/twig-bundle": { 365 | "version": "3.3", 366 | "recipe": { 367 | "repo": "github.com/symfony/recipes", 368 | "branch": "master", 369 | "version": "3.3", 370 | "ref": "369b5b29dc52b2c190002825ae7ec24ab6f962dd" 371 | }, 372 | "files": [ 373 | "config/packages/twig.yaml", 374 | "config/routes/dev/twig.yaml", 375 | "templates/base.html.twig" 376 | ] 377 | }, 378 | "symfony/validator": { 379 | "version": "4.3", 380 | "recipe": { 381 | "repo": "github.com/symfony/recipes", 382 | "branch": "master", 383 | "version": "4.3", 384 | "ref": "d902da3e4952f18d3bf05aab29512eb61cabd869" 385 | }, 386 | "files": [ 387 | "config/packages/test/validator.yaml", 388 | "config/packages/validator.yaml" 389 | ] 390 | }, 391 | "symfony/var-dumper": { 392 | "version": "v4.3.2" 393 | }, 394 | "symfony/var-exporter": { 395 | "version": "v4.3.2" 396 | }, 397 | "symfony/web-link": { 398 | "version": "v4.3.2" 399 | }, 400 | "symfony/web-profiler-bundle": { 401 | "version": "3.3", 402 | "recipe": { 403 | "repo": "github.com/symfony/recipes", 404 | "branch": "master", 405 | "version": "3.3", 406 | "ref": "6bdfa1a95f6b2e677ab985cd1af2eae35d62e0f6" 407 | }, 408 | "files": [ 409 | "config/packages/dev/web_profiler.yaml", 410 | "config/packages/test/web_profiler.yaml", 411 | "config/routes/dev/web_profiler.yaml" 412 | ] 413 | }, 414 | "symfony/web-server-bundle": { 415 | "version": "3.3", 416 | "recipe": { 417 | "repo": "github.com/symfony/recipes", 418 | "branch": "master", 419 | "version": "3.3", 420 | "ref": "dae9b39fd6717970be7601101ce5aa960bf53d9a" 421 | } 422 | }, 423 | "symfony/yaml": { 424 | "version": "v4.3.2" 425 | }, 426 | "twig/twig": { 427 | "version": "v2.11.3" 428 | }, 429 | "webmozart/assert": { 430 | "version": "1.4.0" 431 | } 432 | } 433 | --------------------------------------------------------------------------------