├── var ├── cache │ └── .gitkeep ├── logs │ └── .gitkeep └── SymfonyRequirements.php ├── src ├── .htaccess └── AppBundle │ ├── Resources │ └── config │ │ ├── services.yml │ │ └── routing.yml │ ├── AppBundle.php │ ├── DependencyInjection │ └── AppExtension.php │ ├── Tests │ └── Command │ │ └── TokenCommandTest.php │ ├── Command │ └── TokenCommand.php │ ├── Controller │ ├── TokenRestController.php │ ├── UserRoleRestController.php │ └── UserRestController.php │ └── Entity │ └── User.php ├── web ├── favicon.ico ├── apple-touch-icon.png ├── robots.txt ├── app.php ├── app_dev.php ├── .htaccess └── config.php ├── app ├── Resources │ ├── doc │ │ ├── shot.png │ │ ├── post_user.png │ │ └── index.md │ └── views │ │ └── base.html.twig ├── .htaccess ├── AppCache.php ├── config │ ├── fos │ │ ├── fos_user.yml │ │ └── fos_rest.yml │ ├── services.yml │ ├── config_test.yml │ ├── routing_dev.yml │ ├── routing.yml │ ├── parameters.yml.dist │ ├── config_prod.yml │ ├── jms │ │ └── jms.yml │ ├── security.yml │ ├── config_dev.yml │ └── config.yml ├── autoload.php └── AppKernel.php ├── .travis.yml ├── .jshintrc ├── .gitignore ├── .csslintrc ├── bin ├── console └── symfony_requirements ├── README.md ├── LICENSE └── composer.json /var/cache/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /var/logs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/.htaccess: -------------------------------------------------------------------------------- 1 | deny from all -------------------------------------------------------------------------------- /src/AppBundle/Resources/config/services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdiaz/FOSRestBundleByExample/HEAD/web/favicon.ico -------------------------------------------------------------------------------- /web/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdiaz/FOSRestBundleByExample/HEAD/web/apple-touch-icon.png -------------------------------------------------------------------------------- /app/Resources/doc/shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdiaz/FOSRestBundleByExample/HEAD/app/Resources/doc/shot.png -------------------------------------------------------------------------------- /app/Resources/doc/post_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdiaz/FOSRestBundleByExample/HEAD/app/Resources/doc/post_user.png -------------------------------------------------------------------------------- /web/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 3 | 4 | User-agent: * 5 | -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Require all denied 3 | 4 | 5 | Order deny,allow 6 | Deny from all 7 | 8 | -------------------------------------------------------------------------------- /app/AppCache.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}Welcome!{% endblock %} 6 | {% block stylesheets %}{% endblock %} 7 | 8 | 9 | 10 | {% block body %}{% endblock %} 11 | {% block javascripts %}{% endblock %} 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/AppBundle/AppBundle.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | */ 12 | 13 | namespace AppBundle; 14 | 15 | use Symfony\Component\HttpKernel\Bundle\Bundle; 16 | 17 | class AppBundle extends Bundle 18 | { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/config/routing.yml: -------------------------------------------------------------------------------- 1 | service_token: 2 | type: rest 3 | prefix: /v1 4 | resource: AppBundle\Controller\TokenRestController 5 | name_prefix: api_1_ # naming collision 6 | 7 | service_user: 8 | type: rest 9 | prefix: /v1 10 | resource: AppBundle\Controller\UserRestController 11 | name_prefix: api_1_ # naming collision 12 | 13 | service_user_role: 14 | type: rest 15 | parent: service_user 16 | resource: AppBundle\Controller\UserRoleRestController -------------------------------------------------------------------------------- /app/config/routing.yml: -------------------------------------------------------------------------------- 1 | restful_services: 2 | resource: "@AppBundle/Resources/config/routing.yml" 3 | type: rest 4 | prefix: /api 5 | 6 | nelmio_apidoc: 7 | resource: "@NelmioApiDocBundle/Resources/config/routing.yml" 8 | prefix: / 9 | 10 | fos_user_security: 11 | resource: "@FOSUserBundle/Resources/config/routing/security.xml" 12 | prefix: /console 13 | 14 | fos_user_resetting: 15 | resource: "@FOSUserBundle/Resources/config/routing/resetting.xml" 16 | prefix: /console/resetting -------------------------------------------------------------------------------- /app/config/parameters.yml.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | database_driver: pdo_mysql 3 | database_host: 127.0.0.1 4 | database_port: ~ 5 | database_name: symfony 6 | database_user: root 7 | database_password: ~ 8 | 9 | mailer_transport: smtp 10 | mailer_host: 127.0.0.1 11 | mailer_user: ~ 12 | mailer_password: ~ 13 | 14 | locale: en 15 | secret: ThisTokenIsNotSoSecretChangeIt 16 | 17 | debug_toolbar: true 18 | debug_redirects: false 19 | use_assetic_controller: true 20 | -------------------------------------------------------------------------------- /app/config/config_prod.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config.yml } 3 | 4 | #framework: 5 | # validation: 6 | # cache: apc 7 | 8 | #doctrine: 9 | # orm: 10 | # metadata_cache_driver: apc 11 | # result_cache_driver: apc 12 | # query_cache_driver: apc 13 | 14 | monolog: 15 | handlers: 16 | main: 17 | type: fingers_crossed 18 | action_level: error 19 | handler: nested 20 | nested: 21 | type: stream 22 | path: "%kernel.logs_dir%/%kernel.environment%.log" 23 | level: debug 24 | console: 25 | type: console 26 | -------------------------------------------------------------------------------- /app/config/jms/jms.yml: -------------------------------------------------------------------------------- 1 | # JMS\AopBundle Configuration 2 | jms_aop: 3 | cache_dir: %kernel.cache_dir%/jms_aop 4 | 5 | # JMS\SecurityExtraBundle Configuration 6 | jms_security_extra: 7 | secure_all_services: false 8 | enable_iddqd_attribute: false 9 | expressions: true 10 | 11 | # JMS\DiExtraBundle Configuration 12 | jms_di_extra: 13 | locations: 14 | all_bundles: false 15 | bundles: [AppBundle] 16 | directories: ["%kernel.root_dir%/../src"] 17 | 18 | # Configuration 19 | jms_serializer: 20 | metadata: 21 | cache: file 22 | debug: "%kernel.debug%" 23 | file_cache: 24 | dir: "%kernel.cache_dir%/serializer" 25 | auto_detection: true -------------------------------------------------------------------------------- /app/config/security.yml: -------------------------------------------------------------------------------- 1 | security: 2 | encoders: 3 | FOS\UserBundle\Model\UserInterface: bcrypt 4 | 5 | role_hierarchy: 6 | ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_API, ROLE_ALLOWED_TO_SWITCH] 7 | 8 | providers: 9 | fos_userbundle: 10 | id: fos_user.user_provider.username 11 | 12 | firewalls: 13 | 14 | dev: 15 | pattern: ^/(_(profiler|wdt)|css|images|js)/ 16 | security: false 17 | 18 | wsse_secured: 19 | pattern: (.*)/(role)(.*) 20 | context: user 21 | stateless: true 22 | wsse: 23 | nonce_dir: null 24 | provider: fos_userbundle 25 | lifetime: 600 26 | anonymous: false 27 | 28 | access_control: 29 | - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY } 30 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev'); 19 | $debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod'; 20 | 21 | if ($debug) { 22 | Debug::enable(); 23 | } 24 | 25 | $kernel = new AppKernel($env, $debug); 26 | $application = new Application($kernel); 27 | $application->run($input); 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FOSRestBundleByExample 2 | ====================== 3 | 4 | FOSRestBundle example project with Symfony 2.6 standard distribution. 5 | 6 | **Note:** Contributions and examples welcome. 7 | 8 | **Caution:** This example project is developed in sync with [symfony's repository](https://github.com/symfony/symfony). 9 | 10 | [![Build Status](https://secure.travis-ci.org/sdiaz/FOSRestBundleByExample.png?branch=master)](http://travis-ci.org/sdiaz/FOSRestBundleByExample) 11 | 12 | Documentation 13 | ------------- 14 | 15 | The bulk of the documentation will be stored in the `app/Resources/doc/index.md` 16 | file in this bundle: 17 | 18 | [Read the Documentation for master](app/Resources/doc/index.md) 19 | 20 | After installation you will find in http://localhost/app_dev.php the Nelmio Api Doc with the resources available. 21 | 22 | ![API Example](app/Resources/doc/shot.png) 23 | 24 | License 25 | ------- 26 | 27 | This bundle is under the MIT license. See the complete license in the bundle: 28 | 29 | [LICENSE](LICENSE) 30 | -------------------------------------------------------------------------------- /app/config/fos/fos_rest.yml: -------------------------------------------------------------------------------- 1 | fos_rest: 2 | param_fetcher_listener: true 3 | body_listener: true 4 | format_listener: 5 | rules: 6 | - { path: ^/, priorities: [ html, json, xml ], fallback_format: ~, prefer_extension: true } 7 | 8 | body_converter: 9 | enabled: true 10 | validate: true 11 | 12 | view: 13 | view_response_listener: 'force' 14 | formats: 15 | xml: true 16 | json: true 17 | templating_formats: 18 | html: true 19 | 20 | exception: 21 | codes: 22 | 'Symfony\Component\Routing\Exception\ResourceNotFoundException': 404 23 | 'Doctrine\ORM\OptimisticLockException': HTTP_CONFLICT 24 | messages: 25 | 'Symfony\Component\Routing\Exception\ResourceNotFoundException': true 26 | allowed_methods_listener: true 27 | access_denied_listener: 28 | json: true 29 | 30 | disable_csrf_role: ROLE_API 31 | 32 | 33 | nelmio_api_doc: 34 | name: API Documentation -------------------------------------------------------------------------------- /app/config/config_dev.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config.yml } 3 | 4 | framework: 5 | router: 6 | resource: "%kernel.root_dir%/config/routing_dev.yml" 7 | strict_requirements: true 8 | profiler: { only_exceptions: false } 9 | 10 | web_profiler: 11 | toolbar: %debug_toolbar% 12 | intercept_redirects: %debug_redirects% 13 | 14 | monolog: 15 | handlers: 16 | main: 17 | type: stream 18 | path: "%kernel.logs_dir%/%kernel.environment%.log" 19 | level: debug 20 | console: 21 | type: console 22 | bubble: false 23 | # uncomment to get logging in your browser 24 | # you may have to allow bigger header sizes in your Web server configuration 25 | #firephp: 26 | # type: firephp 27 | # level: info 28 | #chromephp: 29 | # type: chromephp 30 | # level: info 31 | 32 | assetic: 33 | use_controller: %use_assetic_controller% 34 | 35 | #swiftmailer: 36 | # delivery_address: me@example.com 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Santiago Diaz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /web/app.php: -------------------------------------------------------------------------------- 1 | unregister(); 15 | $apcLoader->register(true); 16 | */ 17 | 18 | require_once __DIR__.'/../app/AppKernel.php'; 19 | //require_once __DIR__.'/../app/AppCache.php'; 20 | 21 | $kernel = new AppKernel('prod', false); 22 | $kernel->loadClassCache(); 23 | //$kernel = new AppCache($kernel); 24 | 25 | // When using the HttpCache, you need to call the method in your front controller instead of relying on the configuration parameter 26 | //Request::enableHttpMethodParameterOverride(); 27 | $request = Request::createFromGlobals(); 28 | $response = $kernel->handle($request); 29 | $response->send(); 30 | $kernel->terminate($request, $response); 31 | -------------------------------------------------------------------------------- /src/AppBundle/DependencyInjection/AppExtension.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | */ 12 | 13 | namespace AppBundle\DependencyInjection; 14 | 15 | use Symfony\Component\DependencyInjection\ContainerBuilder; 16 | use Symfony\Component\Config\FileLocator; 17 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; 18 | use Symfony\Component\DependencyInjection\Loader; 19 | 20 | /** 21 | * This is the class that loads and manages your bundle configuration 22 | * 23 | * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} 24 | */ 25 | class AppExtension extends Extension 26 | { 27 | 28 | /** 29 | * {@inheritDoc} 30 | */ 31 | public function load(array $configs, ContainerBuilder $container) 32 | { 33 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); 34 | $loader->load('services.yml'); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /web/app_dev.php: -------------------------------------------------------------------------------- 1 | loadClassCache(); 27 | $request = Request::createFromGlobals(); 28 | $response = $kernel->handle($request); 29 | $response->send(); 30 | $kernel->terminate($request, $response); 31 | -------------------------------------------------------------------------------- /app/Resources/doc/index.md: -------------------------------------------------------------------------------- 1 | # Documentation # 2 | 3 | This example project is based on Symfony Standard distribution. The project aims to show how to build powerful restful apis mainly with : 4 | 5 | * [FOSRestBundle](https://github.com/FriendsOfSymfony/FOSRestBundle) : This Bundle provides various tools to rapidly develop RESTful API's with Symfony2. 6 | * [FOSUserBundle](https://github.com/FriendsOfSymfony/FOSUserBundle) : Provides user management for your Symfony2 Project. Compatible with Doctrine ORM & ODM, and Propel. 7 | * [NelmioApiDocBundle](https://github.com/nelmio/NelmioApiDocBundle) : Generates documentation for your REST API from annotations. 8 | * [MopaWSSEAuthenticationBundle](https://github.com/phiamo/MopaWSSEAuthenticationBundle) : Symfony2 bundle to implement WSSE authentication. 9 | 10 | # Installation # 11 | 12 | Just clone the project and execute: 13 | 14 | curl -sS http://getcomposer.org/installer | php && php composer.phar install 15 | build/travis.sh 16 | 17 | Remember to set the database password properly in *parameters.yml*. 18 | 19 | Now you will able to list the resources at http://localhost/app_dev.php 20 | 21 | # Checking the Restful API # 22 | 23 | Nelmio API Doc provides you with a *sandbox* that you can use to check the controllers. There are services under @Secure interception that need authentication. 24 | 25 | As an example, the following screenshot show the interface while posting a new user: 26 | 27 | ![POST User](post_user.png) 28 | 29 | # Testing # 30 | 31 | Just run phpunit from console: 32 | 33 | build/travis.sh 34 | phpunit -c build 35 | 36 | -------------------------------------------------------------------------------- /src/AppBundle/Tests/Command/TokenCommandTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | */ 12 | 13 | namespace AppBundle\Tests\Command; 14 | 15 | use Symfony\Component\Console\Tester\CommandTester; 16 | use Symfony\Bundle\FrameworkBundle\Console\Application; 17 | use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; 18 | use AppBundle\Command\TokenCommand; 19 | 20 | class TokenCommandTest extends KernelTestCase 21 | { 22 | 23 | public function testExecute() 24 | { 25 | $kernel = $this->createKernel(); 26 | $kernel->boot(); 27 | 28 | $application = new Application($kernel); 29 | $application->add(new TokenCommand()); 30 | 31 | $command = $application->find('app:token:create'); 32 | $commandTester = new CommandTester($command); 33 | $commandTester->execute( 34 | array( 35 | 'username' => 'admin', 36 | 'password' => 'password', 37 | ) 38 | ); 39 | 40 | $this->assertRegExp('/WSSE/', $commandTester->getDisplay()); 41 | } 42 | 43 | public function testFailExecute() 44 | { 45 | $kernel = $this->createKernel(); 46 | $kernel->boot(); 47 | 48 | $application = new Application($kernel); 49 | $application->add(new TokenCommand()); 50 | 51 | $command = $application->find('app:token:create'); 52 | $commandTester = new CommandTester($command); 53 | $commandTester->execute( 54 | array( 55 | 'username' => 'nonexistenuser', 56 | 'password' => 'password', 57 | ) 58 | ); 59 | 60 | $this->assertRegExp('/User not found/', $commandTester->getDisplay()); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /app/AppKernel.php: -------------------------------------------------------------------------------- 1 | getEnvironment(), array('dev', 'test'))) { 32 | $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); 33 | $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); 34 | $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); 35 | $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); 36 | } 37 | 38 | return $bundles; 39 | } 40 | 41 | public function registerContainerConfiguration(LoaderInterface $loader) 42 | { 43 | $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/AppBundle/Command/TokenCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | */ 12 | 13 | namespace AppBundle\Command; 14 | 15 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; 16 | use Symfony\Component\Console\Input\InputArgument; 17 | use Symfony\Component\Console\Input\InputInterface; 18 | use Symfony\Component\Console\Output\OutputInterface; 19 | use AppBundle\Entity\User as User; 20 | 21 | class TokenCommand extends ContainerAwareCommand 22 | { 23 | 24 | protected function configure() 25 | { 26 | $this 27 | ->setName('app:token:create') 28 | ->setDescription('Create a token') 29 | ->addArgument('username', InputArgument::REQUIRED, 'The Username') 30 | ->addArgument('password', InputArgument::REQUIRED, 'The Password') 31 | ->setHelp( 32 | <<app:token:create command generates a valid X-WSSE token for a user. 34 | 35 | php bin/console app:token:create user password 36 | EOT 37 | ); 38 | } 39 | 40 | protected function execute(InputInterface $input, OutputInterface $output) 41 | { 42 | 43 | $userManager = $this->getContainer()->get('fos_user.user_manager'); 44 | $user = $userManager->findUserByUsername($input->getArgument('username')); 45 | 46 | if (!$user instanceof User) { 47 | $output->writeln("User not found."); 48 | } else { 49 | 50 | $factory = $this->getContainer()->get('security.encoder_factory'); 51 | 52 | $encoder = $factory->getEncoder($user); 53 | $password = $encoder->encodePassword($input->getArgument('password'), $user->getSalt()); 54 | 55 | $created = date('c'); 56 | $nonce = substr(md5(uniqid('nonce_', true)), 0, 16); 57 | $nonceSixtyFour = base64_encode($nonce); 58 | $passwordDigest = base64_encode(sha1($nonce . $created . $password, true)); 59 | 60 | $token = sprintf( 61 | 'X-WSSE : UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"', 62 | $input->getArgument('username'), 63 | $passwordDigest, 64 | $nonceSixtyFour, 65 | $created 66 | ); 67 | 68 | $output->writeln($token); 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /web/.htaccess: -------------------------------------------------------------------------------- 1 | # Use the front controller as index file. It serves as a fallback solution when 2 | # every other rewrite/redirect fails (e.g. in an aliased environment without 3 | # mod_rewrite). Additionally, this reduces the matching process for the 4 | # start page (path "/") because otherwise Apache will apply the rewriting rules 5 | # to each configured DirectoryIndex file (e.g. index.php, index.html, index.pl). 6 | DirectoryIndex app.php 7 | 8 | 9 | RewriteEngine On 10 | 11 | # Determine the RewriteBase automatically and set it as environment variable. 12 | # If you are using Apache aliases to do mass virtual hosting or installed the 13 | # project in a subdirectory, the base path will be prepended to allow proper 14 | # resolution of the app.php file and to redirect to the correct URI. It will 15 | # work in environments without path prefix as well, providing a safe, one-size 16 | # fits all solution. But as you do not need it in this case, you can comment 17 | # the following 2 lines to eliminate the overhead. 18 | RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$ 19 | RewriteRule ^(.*) - [E=BASE:%1] 20 | 21 | # Sets the HTTP_AUTHORIZATION header removed by apache 22 | RewriteCond %{HTTP:Authorization} . 23 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 24 | 25 | # Redirect to URI without front controller to prevent duplicate content 26 | # (with and without `/app.php`). Only do this redirect on the initial 27 | # rewrite by Apache and not on subsequent cycles. Otherwise we would get an 28 | # endless redirect loop (request -> rewrite to front controller -> 29 | # redirect -> request -> ...). 30 | # So in case you get a "too many redirects" error or you always get redirected 31 | # to the start page because your Apache does not expose the REDIRECT_STATUS 32 | # environment variable, you have 2 choices: 33 | # - disable this feature by commenting the following 2 lines or 34 | # - use Apache >= 2.3.9 and replace all L flags by END flags and remove the 35 | # following RewriteCond (best solution) 36 | RewriteCond %{ENV:REDIRECT_STATUS} ^$ 37 | RewriteRule ^app\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L] 38 | 39 | # If the requested filename exists, simply serve it. 40 | # We only want to let Apache serve files and not directories. 41 | RewriteCond %{REQUEST_FILENAME} -f 42 | RewriteRule .? - [L] 43 | 44 | # Rewrite all other queries to the front controller. 45 | RewriteRule .? %{ENV:BASE}/app.php [L] 46 | 47 | 48 | 49 | 50 | # When mod_rewrite is not available, we instruct a temporary redirect of 51 | # the start page to the front controller explicitly so that the website 52 | # and the generated links can still be used. 53 | RedirectMatch 302 ^/$ /app.php/ 54 | # RedirectTemp cannot be used instead 55 | 56 | 57 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "symfony/framework-standard-edition", 3 | "license": "MIT", 4 | "type": "project", 5 | "description": "The \"Symfony Standard Edition\" distribution", 6 | "autoload": { 7 | "psr-0": { 8 | "": "src/", 9 | "SymfonyStandard": "app/" 10 | } 11 | }, 12 | "require": { 13 | "php": ">=5.3.3", 14 | "symfony/symfony": "2.6.*", 15 | "doctrine/orm": "~2.2,>=2.2.3", 16 | "doctrine/doctrine-bundle": "~1.2", 17 | "twig/extensions": "~1.0", 18 | "symfony/assetic-bundle": "~2.3", 19 | "symfony/swiftmailer-bundle": "~2.3", 20 | "symfony/monolog-bundle": "~2.4", 21 | "sensio/distribution-bundle": "~3.0,>=3.0.12", 22 | "sensio/framework-extra-bundle": "~3.0", 23 | "incenteev/composer-parameter-handler": "~2.0", 24 | "jms/security-extra-bundle": "1.5.*", 25 | "jms/di-extra-bundle": "1.5.*", 26 | "jms/serializer-bundle": "0.13.*", 27 | "stof/doctrine-extensions-bundle": "1.1.*@dev", 28 | "friendsofsymfony/user-bundle": "1.3.*", 29 | "friendsofsymfony/rest-bundle": "1.5.*", 30 | "willdurand/rest-extra-bundle": "1.0.*", 31 | "nelmio/api-doc-bundle": "2.7.0", 32 | "mopa/wsse-authentication-bundle": "dev-master", 33 | "sami/sami": "3.0.*" 34 | }, 35 | "require-dev": { 36 | "sensio/generator-bundle": "~2.3" 37 | }, 38 | "scripts": { 39 | "post-root-package-install": [ 40 | "SymfonyStandard\\Composer::hookRootPackageInstall" 41 | ], 42 | "post-install-cmd": [ 43 | "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", 44 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", 45 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", 46 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", 47 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", 48 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles" 49 | ], 50 | "post-update-cmd": [ 51 | "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", 52 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", 53 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", 54 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", 55 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", 56 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles" 57 | ] 58 | }, 59 | "config": { 60 | "bin-dir": "bin" 61 | }, 62 | "extra": { 63 | "symfony-app-dir": "app", 64 | "symfony-bin-dir": "bin", 65 | "symfony-var-dir": "var", 66 | "symfony-web-dir": "web", 67 | "symfony-assets-install": "relative", 68 | "incenteev-parameters": { 69 | "file": "app/config/parameters.yml" 70 | }, 71 | "branch-alias": { 72 | "dev-master": "2.6-dev" 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/AppBundle/Controller/TokenRestController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | */ 12 | 13 | namespace AppBundle\Controller; 14 | 15 | use FOS\RestBundle\Controller\FOSRestController; 16 | use FOS\RestBundle\Controller\Annotations\RequestParam; 17 | use FOS\RestBundle\View\View; 18 | use FOS\RestBundle\Request\ParamFetcher; 19 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; 20 | use AppBundle\Entity\User as User; 21 | 22 | class TokenRestController extends FOSRestController 23 | { 24 | 25 | /** 26 | * Create a Token from the submitted data.
27 | * 28 | * @ApiDoc( 29 | * resource = true, 30 | * description = "Creates a new token from the submitted data.", 31 | * statusCodes = { 32 | * 200 = "Returned when successful", 33 | * 400 = "Returned when the form has errors" 34 | * } 35 | * ) 36 | * 37 | * @param ParamFetcher $paramFetcher Paramfetcher 38 | * 39 | * @RequestParam(name="username", nullable=false, strict=true, description="username.") 40 | * @RequestParam(name="password", nullable=false, strict=true, description="password.") 41 | * @RequestParam(name="salt", nullable=false, strict=true, description="salt.") 42 | * 43 | * @return View 44 | */ 45 | public function postTokenAction(ParamFetcher $paramFetcher) 46 | { 47 | 48 | $view = View::create(); 49 | 50 | $userManager = $this->get('fos_user.user_manager'); 51 | $user = $userManager->findUserByUsername($paramFetcher->get('username')); 52 | 53 | if (!$user instanceof User) { 54 | $view->setStatusCode(404)->setData("Data received succesfully but with errors."); 55 | 56 | return $view; 57 | } 58 | 59 | $factory = $this->get('security.encoder_factory'); 60 | 61 | $encoder = $factory->getEncoder($user); 62 | $password = $encoder->encodePassword($paramFetcher->get('password'), $paramFetcher->get('salt')); 63 | 64 | $header = $this->generateToken($paramFetcher->get('username'), $password); 65 | $data = array('X-WSSE' => $header); 66 | $view->setHeader("Authorization", 'WSSE profile="UsernameToken"'); 67 | $view->setHeader("X-WSSE", $header); 68 | $view->setStatusCode(200)->setData($data); 69 | 70 | return $view; 71 | } 72 | 73 | /** 74 | * Generate token for username given 75 | * 76 | * @param string $username username 77 | * @param string $password password with salt included 78 | * @return string 79 | */ 80 | private function generateToken($username, $password) 81 | { 82 | $created = date('c'); 83 | $nonce = substr(md5(uniqid('nonce_', true)), 0, 16); 84 | $nonceSixtyFour = base64_encode($nonce); 85 | $passwordDigest = base64_encode(sha1($nonce . $created . $password, true)); 86 | 87 | $token = sprintf( 88 | 'UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"', 89 | $username, 90 | $passwordDigest, 91 | $nonceSixtyFour, 92 | $created 93 | ); 94 | 95 | return $token; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/AppBundle/Entity/User.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | */ 12 | 13 | namespace AppBundle\Entity; 14 | 15 | use Doctrine\ORM\Mapping as ORM; 16 | use FOS\UserBundle\Entity\User as BaseUser; 17 | use Gedmo\Mapping\Annotation as Gedmo; 18 | 19 | /** 20 | * User 21 | * 22 | * @Gedmo\Loggable 23 | * @ORM\Table() 24 | * @ORM\Entity() 25 | */ 26 | class User extends BaseUser 27 | { 28 | 29 | /** 30 | * @var integer 31 | * 32 | * @ORM\Column(name="id", type="integer") 33 | * @ORM\Id 34 | * @ORM\GeneratedValue(strategy="AUTO") 35 | */ 36 | protected $id; 37 | 38 | /** 39 | * @var string 40 | * 41 | * @Gedmo\Versioned 42 | * @ORM\Column(name="name", type="string", length=255, nullable=true) 43 | */ 44 | protected $name; 45 | 46 | /** 47 | * @var string 48 | * 49 | * @Gedmo\Versioned 50 | * @ORM\Column(name="lastname", type="string", length=255, nullable=true) 51 | */ 52 | protected $lastname; 53 | 54 | /** 55 | * 56 | * @var datetime $created 57 | * 58 | * @Gedmo\Timestampable(on="create") 59 | * @ORM\Column(type="datetime") 60 | */ 61 | protected $created; 62 | 63 | /** 64 | * 65 | * @var datetime $updated 66 | * 67 | * @Gedmo\Timestampable(on="update") 68 | * @ORM\Column(type="datetime") 69 | */ 70 | protected $updated; 71 | 72 | 73 | /** 74 | * Get id 75 | * 76 | * @return integer 77 | */ 78 | public function getId() 79 | { 80 | return $this->id; 81 | } 82 | 83 | /** 84 | * Set name 85 | * 86 | * @param string $name 87 | * @return User 88 | */ 89 | public function setName($name) 90 | { 91 | $this->name = $name; 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * Get name 98 | * 99 | * @return string 100 | */ 101 | public function getName() 102 | { 103 | return $this->name; 104 | } 105 | 106 | /** 107 | * Set lastname 108 | * 109 | * @param string $lastname 110 | * @return User 111 | */ 112 | public function setLastname($lastname) 113 | { 114 | $this->lastname = $lastname; 115 | 116 | return $this; 117 | } 118 | 119 | /** 120 | * Get lastname 121 | * 122 | * @return string 123 | */ 124 | public function getLastname() 125 | { 126 | return $this->lastname; 127 | } 128 | 129 | /** 130 | * Set created 131 | * 132 | * @param \DateTime $created 133 | * @return User 134 | */ 135 | public function setCreated($created) 136 | { 137 | $this->created = $created; 138 | 139 | return $this; 140 | } 141 | 142 | /** 143 | * Get created 144 | * 145 | * @return \DateTime 146 | */ 147 | public function getCreated() 148 | { 149 | return $this->created; 150 | } 151 | 152 | /** 153 | * Set updated 154 | * 155 | * @param \DateTime $updated 156 | * @return User 157 | */ 158 | public function setUpdated($updated) 159 | { 160 | $this->updated = $updated; 161 | 162 | return $this; 163 | } 164 | 165 | /** 166 | * Get updated 167 | * 168 | * @return \DateTime 169 | */ 170 | public function getUpdated() 171 | { 172 | return $this->updated; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/AppBundle/Controller/UserRoleRestController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | */ 12 | 13 | namespace AppBundle\Controller; 14 | 15 | use FOS\RestBundle\Controller\FOSRestController; 16 | use FOS\RestBundle\Controller\Annotations\RequestParam; 17 | use FOS\RestBundle\View\View; 18 | use FOS\RestBundle\Request\ParamFetcher; 19 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; 20 | use JMS\SecurityExtraBundle\Annotation\Secure; 21 | 22 | class UserRoleRestController extends FOSRestController 23 | { 24 | 25 | /** 26 | * Return all roles for an user identified by username/email. 27 | * 28 | * @Secure(roles="ROLE_API") 29 | * @ApiDoc( 30 | * resource = true, 31 | * description = "Return an user identified by username/email", 32 | * statusCodes = { 33 | * 200 = "Returned when successful", 34 | * 404 = "Returned when the user is not found" 35 | * } 36 | * ) 37 | * 38 | * @param string $slug username or email 39 | * 40 | * @return View 41 | */ 42 | public function getRoleAction($slug) 43 | { 44 | 45 | $userManager = $this->container->get('fos_user.user_manager'); 46 | $entity = $userManager->findUserByUsernameOrEmail($slug); 47 | 48 | if (!$entity) { 49 | throw $this->createNotFoundException('Data not found.'); 50 | } 51 | 52 | $view = View::create(); 53 | $view->setData($entity->getRoles())->setStatusCode(200); 54 | 55 | return $view; 56 | } 57 | 58 | /** 59 | * Create a Role from the submitted data.
60 | * 61 | * @Secure(roles="ROLE_API") 62 | * @ApiDoc( 63 | * resource = true, 64 | * description = "Creates a new role from the submitted data.", 65 | * statusCodes = { 66 | * 200 = "Returned when successful", 67 | * 400 = "Returned when the form has errors" 68 | * } 69 | * ) 70 | * 71 | * @param ParamFetcher $paramFetcher Paramfetcher 72 | * 73 | * @RequestParam(name="slug", nullable=false, strict=true, description="Username or Email.") 74 | * @RequestParam(name="role", nullable=false, strict=true, description="Role.") 75 | * 76 | * @return View 77 | */ 78 | public function postRoleAction(ParamFetcher $paramFetcher) 79 | { 80 | 81 | $userManager = $this->container->get('fos_user.user_manager'); 82 | $entity = $userManager->findUserByUsernameOrEmail($paramFetcher->get('slug')); 83 | 84 | if (!$entity) { 85 | throw $this->createNotFoundException('Data not found.'); 86 | } 87 | 88 | $entity->addRole($paramFetcher->get('role')); 89 | $userManager->updateUser($entity); 90 | 91 | $view = View::create(); 92 | $view->setData($entity->getRoles())->setStatusCode(200); 93 | 94 | return $view; 95 | } 96 | 97 | /** 98 | * Delete a Role from the submitted data.
99 | * 100 | * @Secure(roles="ROLE_API") 101 | * @ApiDoc( 102 | * resource = true, 103 | * description = "Deletes a role from the submitted data.", 104 | * statusCodes = { 105 | * 200 = "Returned when successful", 106 | * 400 = "Returned when the form has errors" 107 | * } 108 | * ) 109 | * 110 | * @param ParamFetcher $paramFetcher Paramfetcher 111 | * 112 | * @RequestParam(name="slug", nullable=false, strict=true, description="Username or Email.") 113 | * @RequestParam(name="role", nullable=false, strict=true, description="Role.") 114 | * 115 | * @return View 116 | */ 117 | public function deleteRoleAction(ParamFetcher $paramFetcher) 118 | { 119 | 120 | $userManager = $this->container->get('fos_user.user_manager'); 121 | $entity = $userManager->findUserByUsernameOrEmail($paramFetcher->get('slug')); 122 | 123 | if (!$entity) { 124 | throw $this->createNotFoundException('Data not found.'); 125 | } 126 | 127 | // We do not check if the Role previously exists. 128 | $entity->removeRole($paramFetcher->get('role')); 129 | $userManager->updateUser($entity); 130 | 131 | $view = View::create(); 132 | $view->setData($entity->getRoles())->setStatusCode(200); 133 | 134 | return $view; 135 | } 136 | 137 | } -------------------------------------------------------------------------------- /bin/symfony_requirements: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getPhpIniConfigPath(); 9 | 10 | echo_title('Symfony2 Requirements Checker'); 11 | 12 | echo '> PHP is using the following php.ini file:'.PHP_EOL; 13 | if ($iniPath) { 14 | echo_style('green', ' '.$iniPath); 15 | } else { 16 | echo_style('warning', ' WARNING: No configuration file (php.ini) used by PHP!'); 17 | } 18 | 19 | echo PHP_EOL.PHP_EOL; 20 | 21 | echo '> Checking Symfony requirements:'.PHP_EOL.' '; 22 | 23 | $messages = array(); 24 | foreach ($symfonyRequirements->getRequirements() as $req) { 25 | /** @var $req Requirement */ 26 | if ($helpText = get_error_message($req, $lineSize)) { 27 | echo_style('red', 'E'); 28 | $messages['error'][] = $helpText; 29 | } else { 30 | echo_style('green', '.'); 31 | } 32 | } 33 | 34 | $checkPassed = empty($messages['error']); 35 | 36 | foreach ($symfonyRequirements->getRecommendations() as $req) { 37 | if ($helpText = get_error_message($req, $lineSize)) { 38 | echo_style('yellow', 'W'); 39 | $messages['warning'][] = $helpText; 40 | } else { 41 | echo_style('green', '.'); 42 | } 43 | } 44 | 45 | if ($checkPassed) { 46 | echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects', true); 47 | } else { 48 | echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects', true); 49 | 50 | echo_title('Fix the following mandatory requirements', 'red'); 51 | 52 | foreach ($messages['error'] as $helpText) { 53 | echo ' * '.$helpText.PHP_EOL; 54 | } 55 | } 56 | 57 | if (!empty($messages['warning'])) { 58 | echo_title('Optional recommendations to improve your setup', 'yellow'); 59 | 60 | foreach ($messages['warning'] as $helpText) { 61 | echo ' * '.$helpText.PHP_EOL; 62 | } 63 | } 64 | 65 | echo PHP_EOL; 66 | echo_style('title', 'Note'); 67 | echo ' The command console could use a different php.ini file'.PHP_EOL; 68 | echo_style('title', '~~~~'); 69 | echo ' than the one used with your web server. To be on the'.PHP_EOL; 70 | echo ' safe side, please check the requirements from your web'.PHP_EOL; 71 | echo ' server using the '; 72 | echo_style('yellow', 'web/config.php'); 73 | echo ' script.'.PHP_EOL; 74 | echo PHP_EOL; 75 | 76 | exit($checkPassed ? 0 : 1); 77 | 78 | function get_error_message(Requirement $requirement, $lineSize) 79 | { 80 | if ($requirement->isFulfilled()) { 81 | return; 82 | } 83 | 84 | $errorMessage = wordwrap($requirement->getTestMessage(), $lineSize - 3, PHP_EOL.' ').PHP_EOL; 85 | $errorMessage .= ' > '.wordwrap($requirement->getHelpText(), $lineSize - 5, PHP_EOL.' > ').PHP_EOL; 86 | 87 | return $errorMessage; 88 | } 89 | 90 | function echo_title($title, $style = null) 91 | { 92 | $style = $style ?: 'title'; 93 | 94 | echo PHP_EOL; 95 | echo_style($style, $title.PHP_EOL); 96 | echo_style($style, str_repeat('~', strlen($title)).PHP_EOL); 97 | echo PHP_EOL; 98 | } 99 | 100 | function echo_style($style, $message) 101 | { 102 | // ANSI color codes 103 | $styles = array( 104 | 'reset' => "\033[0m", 105 | 'red' => "\033[31m", 106 | 'green' => "\033[32m", 107 | 'yellow' => "\033[33m", 108 | 'error' => "\033[37;41m", 109 | 'success' => "\033[37;42m", 110 | 'title' => "\033[34m", 111 | ); 112 | $supports = has_color_support(); 113 | 114 | echo($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : ''); 115 | } 116 | 117 | function echo_block($style, $title, $message) 118 | { 119 | $message = ' '.trim($message).' '; 120 | $width = strlen($message); 121 | 122 | echo PHP_EOL.PHP_EOL; 123 | 124 | echo_style($style, str_repeat(' ', $width).PHP_EOL); 125 | echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT).PHP_EOL); 126 | echo_style($style, str_pad($message, $width, ' ', STR_PAD_RIGHT).PHP_EOL); 127 | echo_style($style, str_repeat(' ', $width).PHP_EOL); 128 | } 129 | 130 | function has_color_support() 131 | { 132 | static $support; 133 | 134 | if (null === $support) { 135 | if (DIRECTORY_SEPARATOR == '\\') { 136 | $support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); 137 | } else { 138 | $support = function_exists('posix_isatty') && @posix_isatty(STDOUT); 139 | } 140 | } 141 | 142 | return $support; 143 | } 144 | -------------------------------------------------------------------------------- /app/config/config.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: parameters.yml } 3 | - { resource: security.yml } 4 | - { resource: services.yml } 5 | 6 | # FriendOfSymfony Bundles 7 | - { resource: fos/fos_rest.yml } 8 | - { resource: fos/fos_user.yml } 9 | 10 | # Others 11 | - { resource: jms/jms.yml } 12 | 13 | framework: 14 | #esi: ~ 15 | translator: { fallback: "%locale%" } 16 | secret: "%secret%" 17 | router: 18 | resource: "%kernel.root_dir%/config/routing.yml" 19 | strict_requirements: ~ 20 | form: true 21 | csrf_protection: true 22 | validation: { enable_annotations: true } 23 | templating: 24 | engines: ['twig'] 25 | #assets_version: SomeVersionScheme 26 | default_locale: "%locale%" 27 | trusted_hosts: ~ 28 | trusted_proxies: ~ 29 | session: 30 | # handler_id set to null will use default session handler from php.ini 31 | handler_id: ~ 32 | fragments: ~ 33 | http_method_override: true 34 | 35 | # Twig Configuration 36 | twig: 37 | debug: "%kernel.debug%" 38 | strict_variables: "%kernel.debug%" 39 | exception_controller: 'FOS\RestBundle\Controller\ExceptionController::showAction' 40 | 41 | # Assetic Configuration 42 | assetic: 43 | debug: "%kernel.debug%" 44 | use_controller: false 45 | bundles: [AppBundle] 46 | #java: /usr/bin/java 47 | filters: 48 | cssrewrite: ~ 49 | #closure: 50 | # jar: "%kernel.root_dir%/Resources/java/compiler.jar" 51 | #yui_css: 52 | # jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar" 53 | 54 | # Doctrine Configuration 55 | doctrine: 56 | dbal: 57 | driver: "%database_driver%" 58 | host: "%database_host%" 59 | port: "%database_port%" 60 | dbname: "%database_name%" 61 | user: "%database_user%" 62 | password: "%database_password%" 63 | server_version: 5.6 # your database server version here 64 | charset: UTF8 65 | mapping_types: 66 | #se mapean los enums como strings 67 | enum: string 68 | 69 | orm: 70 | auto_generate_proxy_classes: "%kernel.debug%" 71 | auto_mapping: true 72 | mappings: 73 | gedmo_translatable: 74 | type: annotation 75 | prefix: Gedmo\Translatable\Entity 76 | dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity" 77 | alias: GedmoTranslatable # this one is optional and will default to the name set for the mapping 78 | is_bundle: false 79 | gedmo_translator: 80 | type: annotation 81 | prefix: Gedmo\Translator\Entity 82 | dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Entity" 83 | alias: GedmoTranslator # this one is optional and will default to the name set for the mapping 84 | is_bundle: false 85 | gedmo_loggable: 86 | type: annotation 87 | prefix: Gedmo\Loggable\Entity 88 | dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity" 89 | alias: GedmoLoggable # this one is optional and will default to the name set for the mapping 90 | is_bundle: false 91 | gedmo_tree: 92 | type: annotation 93 | prefix: Gedmo\Tree\Entity 94 | dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity" 95 | alias: GedmoTree # this one is optional and will default to the name set for the mapping 96 | is_bundle: false 97 | FOSUserBundle: ~ 98 | AppBundle: ~ 99 | 100 | # Swiftmailer Configuration 101 | swiftmailer: 102 | transport: "%mailer_transport%" 103 | host: "%mailer_host%" 104 | username: "%mailer_user%" 105 | password: "%mailer_password%" 106 | spool: { type: memory } 107 | 108 | # Sensio\Bundle\FrameworkExtraBundle Configuration 109 | sensio_framework_extra: 110 | router: { annotations: true } 111 | request: { converters: true, auto_convert: true } 112 | view: { annotations: false } 113 | cache: { annotations: true } 114 | security: { annotations: true } 115 | 116 | #StofDoctrineExtensionsBundle Configuration 117 | stof_doctrine_extensions: 118 | default_locale: %locale% 119 | orm: 120 | default: 121 | translatable: true 122 | timestampable: true 123 | sluggable: true 124 | loggable: true 125 | tree: true 126 | 127 | # Mopa WSSE authentication configuration 128 | mopa_wsse_authentication: 129 | provider_class: Mopa\Bundle\WSSEAuthenticationBundle\Security\Authentication\Provider\WsseAuthenticationProvider 130 | listener_class: Mopa\Bundle\WSSEAuthenticationBundle\Security\Firewall\WsseListener 131 | factory_class: Mopa\Bundle\WSSEAuthenticationBundle\Security\Factory\WsseFactory 132 | -------------------------------------------------------------------------------- /web/config.php: -------------------------------------------------------------------------------- 1 | getFailedRequirements(); 20 | $minorProblems = $symfonyRequirements->getFailedRecommendations(); 21 | 22 | ?> 23 | 24 | 25 | 26 | 27 | 28 | Symfony Configuration 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 | 39 | 40 | 60 |
61 | 62 |
63 |
64 |
65 |

Welcome!

66 |

Welcome to your new Symfony project.

67 |

68 | This script will guide you through the basic configuration of your project. 69 | You can also do the same by editing the ‘app/config/parameters.yml’ file directly. 70 |

71 | 72 | 73 |

Major problems

74 |

Major problems have been detected and must be fixed before continuing:

75 |
    76 | 77 |
  1. getHelpHtml() ?>
  2. 78 | 79 |
80 | 81 | 82 | 83 |

Recommendations

84 |

85 | Additionally, toTo enhance your Symfony experience, 86 | it’s recommended that you fix the following: 87 |

88 |
    89 | 90 |
  1. getHelpHtml() ?>
  2. 91 | 92 |
93 | 94 | 95 | hasPhpIniConfigIssue()): ?> 96 |

* 97 | getPhpIniConfigPath()): ?> 98 | Changes to the php.ini file must be done in "getPhpIniConfigPath() ?>". 99 | 100 | To change settings, create a "php.ini". 101 | 102 |

103 | 104 | 105 | 106 |

Your configuration looks good to run Symfony.

107 | 108 | 109 | 118 |
119 |
120 |
121 |
Symfony Standard Edition
122 |
123 | 124 | 125 | -------------------------------------------------------------------------------- /src/AppBundle/Controller/UserRestController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | */ 12 | 13 | namespace AppBundle\Controller; 14 | 15 | use FOS\RestBundle\Controller\FOSRestController; 16 | use FOS\RestBundle\Controller\Annotations\RequestParam; 17 | use FOS\RestBundle\View\View; 18 | use FOS\RestBundle\Request\ParamFetcher; 19 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; 20 | use JMS\SecurityExtraBundle\Annotation\Secure; 21 | use Symfony\Component\Validator\ConstraintViolationList; 22 | 23 | class UserRestController extends FOSRestController 24 | { 25 | 26 | /** 27 | * Return the overall user list. 28 | * 29 | * @Secure(roles="ROLE_API") 30 | * @ApiDoc( 31 | * resource = true, 32 | * description = "Return the overall User List", 33 | * statusCodes = { 34 | * 200 = "Returned when successful", 35 | * 404 = "Returned when the user is not found" 36 | * } 37 | * ) 38 | * 39 | * @return View 40 | */ 41 | public function getUsersAction() 42 | { 43 | $userManager = $this->container->get('fos_user.user_manager'); 44 | $entity = $userManager->findUsers(); 45 | 46 | if (!$entity) { 47 | throw $this->createNotFoundException('Data not found.'); 48 | } 49 | 50 | $view = View::create(); 51 | $view->setData($entity)->setStatusCode(200); 52 | 53 | return $view; 54 | } 55 | 56 | /** 57 | * Return an user identified by username/email. 58 | * 59 | * @Secure(roles="ROLE_API") 60 | * @ApiDoc( 61 | * resource = true, 62 | * description = "Return an user identified by username/email", 63 | * statusCodes = { 64 | * 200 = "Returned when successful", 65 | * 404 = "Returned when the user is not found" 66 | * } 67 | * ) 68 | * 69 | * @param string $slug username or email 70 | * 71 | * @return View 72 | */ 73 | public function getUserAction($slug) 74 | { 75 | 76 | $userManager = $this->container->get('fos_user.user_manager'); 77 | $entity = $userManager->findUserByUsernameOrEmail($slug); 78 | 79 | if (!$entity) { 80 | throw $this->createNotFoundException('Data not found.'); 81 | } 82 | 83 | $view = View::create(); 84 | $view->setData($entity)->setStatusCode(200); 85 | 86 | return $view; 87 | } 88 | 89 | /** 90 | * Create a User from the submitted data.
91 | * 92 | * @ApiDoc( 93 | * resource = true, 94 | * description = "Creates a new user from the submitted data.", 95 | * statusCodes = { 96 | * 200 = "Returned when successful", 97 | * 400 = "Returned when the form has errors" 98 | * } 99 | * ) 100 | * 101 | * @param ParamFetcher $paramFetcher Paramfetcher 102 | * 103 | * @RequestParam(name="username", nullable=false, strict=true, description="Username.") 104 | * @RequestParam(name="email", nullable=false, strict=true, description="Email.") 105 | * @RequestParam(name="name", nullable=false, strict=true, description="Name.") 106 | * @RequestParam(name="lastname", nullable=false, strict=true, description="Lastname.") 107 | * @RequestParam(name="password", nullable=false, strict=true, description="Plain Password.") 108 | * 109 | * @return View 110 | */ 111 | public function postUserAction(ParamFetcher $paramFetcher) 112 | { 113 | 114 | $userManager = $this->container->get('fos_user.user_manager'); 115 | 116 | $user = $userManager->createUser(); 117 | $user->setUsername($paramFetcher->get('username')); 118 | $user->setEmail($paramFetcher->get('email')); 119 | $user->setPlainPassword($paramFetcher->get('password')); 120 | $user->setName($paramFetcher->get('name')); 121 | $user->setLastname($paramFetcher->get('lastname')); 122 | $user->setEnabled(true); 123 | $user->addRole('ROLE_API'); 124 | 125 | $view = View::create(); 126 | 127 | $errors = $this->get('validator')->validate($user, array('Registration')); 128 | 129 | if (count($errors) == 0) { 130 | $userManager->updateUser($user); 131 | $view->setData($user)->setStatusCode(200); 132 | return $view; 133 | } else { 134 | $view = $this->getErrorsView($errors); 135 | return $view; 136 | } 137 | } 138 | 139 | /** 140 | * Update a User from the submitted data by ID.
141 | * 142 | * @Secure(roles="ROLE_API") 143 | * @ApiDoc( 144 | * resource = true, 145 | * description = "Updates a user from the submitted data by ID.", 146 | * statusCodes = { 147 | * 200 = "Returned when successful", 148 | * 400 = "Returned when the form has errors" 149 | * } 150 | * ) 151 | * 152 | * @param ParamFetcher $paramFetcher Paramfetcher 153 | * 154 | * @RequestParam(name="id", nullable=false, strict=true, description="id.") 155 | * @RequestParam(name="username", nullable=true, strict=true, description="Username.") 156 | * @RequestParam(name="email", nullable=true, strict=true, description="Email.") 157 | * @RequestParam(name="name", nullable=true, strict=true, description="Name.") 158 | * @RequestParam(name="lastname", nullable=true, strict=true, description="Lastname.") 159 | * @RequestParam(name="password", nullable=true, strict=true, description="Plain Password.") 160 | * 161 | * @return View 162 | */ 163 | public function putUserAction(ParamFetcher $paramFetcher) 164 | { 165 | 166 | $entity = $this->getDoctrine()->getRepository('AppBundle\Entity\User')->findOneBy( 167 | array('id' => $paramFetcher->get('id')) 168 | ); 169 | 170 | $userManager = $this->container->get('fos_user.user_manager'); 171 | $user = $userManager->findUserByUsername($entity->getUsername()); 172 | 173 | if($paramFetcher->get('username')){ $user->setUsername($paramFetcher->get('username')); } 174 | if($paramFetcher->get('email')){$user->setEmail($paramFetcher->get('email')); } 175 | if($paramFetcher->get('password')){$user->setPlainPassword($paramFetcher->get('password')); } 176 | if($paramFetcher->get('name')){$user->setName($paramFetcher->get('name')); } 177 | if($paramFetcher->get('lastname')){$user->setLastname($paramFetcher->get('lastname')); } 178 | 179 | $view = View::create(); 180 | 181 | $errors = $this->get('validator')->validate($user, array('Update')); 182 | 183 | if (count($errors) == 0) { 184 | $userManager->updateUser($user); 185 | $view->setData($user)->setStatusCode(200); 186 | return $view; 187 | } else { 188 | $view = $this->getErrorsView($errors); 189 | return $view; 190 | } 191 | } 192 | 193 | /** 194 | * Delete an user identified by username/email. 195 | * 196 | * @Secure(roles="ROLE_API") 197 | * @ApiDoc( 198 | * resource = true, 199 | * description = "Delete an user identified by username/email", 200 | * statusCodes = { 201 | * 200 = "Returned when successful", 202 | * 404 = "Returned when the user is not found" 203 | * } 204 | * ) 205 | * 206 | * @param string $slug username or email 207 | * 208 | * @return View 209 | */ 210 | public function deleteUserAction($slug) 211 | { 212 | $userManager = $this->container->get('fos_user.user_manager'); 213 | $entity = $userManager->findUserByUsernameOrEmail($slug); 214 | 215 | if (!$entity) { 216 | throw $this->createNotFoundException('Data not found.'); 217 | } 218 | 219 | $userManager->deleteUser($entity); 220 | 221 | $view = View::create(); 222 | $view->setData("User deteled.")->setStatusCode(204); 223 | 224 | return $view; 225 | } 226 | 227 | /** 228 | * Get User Salt. 229 | * 230 | * @ApiDoc( 231 | * resource = true, 232 | * description = "Get user salt by its username", 233 | * statusCodes = { 234 | * 200 = "Returned when successful", 235 | * 404 = "Returned when the user is not found" 236 | * } 237 | * ) 238 | * 239 | * @param string $id the user username 240 | * 241 | * @return View 242 | */ 243 | public function getUserSaltAction($slug) 244 | { 245 | 246 | $entity = $this->getDoctrine()->getRepository('AppBundle\Entity\User')->findOneBy( 247 | array('username' => $slug) 248 | ); 249 | 250 | if (!$entity) { 251 | throw $this->createNotFoundException('Data not found.'); 252 | } 253 | 254 | $salt = $entity->getSalt(); 255 | 256 | $view = View::create(); 257 | $view->setData(array('salt' => $salt))->setStatusCode(200); 258 | 259 | return $view; 260 | } 261 | 262 | 263 | /** 264 | * Get the validation errors 265 | * 266 | * @param ConstraintViolationList $errors Validator error list 267 | * 268 | * @return View 269 | */ 270 | protected function getErrorsView(ConstraintViolationList $errors) 271 | { 272 | $msgs = array(); 273 | $errorIterator = $errors->getIterator(); 274 | foreach ($errorIterator as $validationError) { 275 | $msg = $validationError->getMessage(); 276 | $params = $validationError->getMessageParameters(); 277 | $msgs[$validationError->getPropertyPath()][] = $this->get('translator')->trans($msg, $params, 'validators'); 278 | } 279 | $view = View::create($msgs); 280 | $view->setStatusCode(400); 281 | 282 | return $view; 283 | } 284 | 285 | } 286 | -------------------------------------------------------------------------------- /var/SymfonyRequirements.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | /* 13 | * Users of PHP 5.2 should be able to run the requirements checks. 14 | * This is why the file and all classes must be compatible with PHP 5.2+ 15 | * (e.g. not using namespaces and closures). 16 | * 17 | * ************** CAUTION ************** 18 | * 19 | * DO NOT EDIT THIS FILE as it will be overridden by Composer as part of 20 | * the installation/update process. The original file resides in the 21 | * SensioDistributionBundle. 22 | * 23 | * ************** CAUTION ************** 24 | */ 25 | 26 | /** 27 | * Represents a single PHP requirement, e.g. an installed extension. 28 | * It can be a mandatory requirement or an optional recommendation. 29 | * There is a special subclass, named PhpIniRequirement, to check a php.ini configuration. 30 | * 31 | * @author Tobias Schultze 32 | */ 33 | class Requirement 34 | { 35 | private $fulfilled; 36 | private $testMessage; 37 | private $helpText; 38 | private $helpHtml; 39 | private $optional; 40 | 41 | /** 42 | * Constructor that initializes the requirement. 43 | * 44 | * @param bool $fulfilled Whether the requirement is fulfilled 45 | * @param string $testMessage The message for testing the requirement 46 | * @param string $helpHtml The help text formatted in HTML for resolving the problem 47 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 48 | * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement 49 | */ 50 | public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false) 51 | { 52 | $this->fulfilled = (bool) $fulfilled; 53 | $this->testMessage = (string) $testMessage; 54 | $this->helpHtml = (string) $helpHtml; 55 | $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText; 56 | $this->optional = (bool) $optional; 57 | } 58 | 59 | /** 60 | * Returns whether the requirement is fulfilled. 61 | * 62 | * @return bool true if fulfilled, otherwise false 63 | */ 64 | public function isFulfilled() 65 | { 66 | return $this->fulfilled; 67 | } 68 | 69 | /** 70 | * Returns the message for testing the requirement. 71 | * 72 | * @return string The test message 73 | */ 74 | public function getTestMessage() 75 | { 76 | return $this->testMessage; 77 | } 78 | 79 | /** 80 | * Returns the help text for resolving the problem 81 | * 82 | * @return string The help text 83 | */ 84 | public function getHelpText() 85 | { 86 | return $this->helpText; 87 | } 88 | 89 | /** 90 | * Returns the help text formatted in HTML. 91 | * 92 | * @return string The HTML help 93 | */ 94 | public function getHelpHtml() 95 | { 96 | return $this->helpHtml; 97 | } 98 | 99 | /** 100 | * Returns whether this is only an optional recommendation and not a mandatory requirement. 101 | * 102 | * @return bool true if optional, false if mandatory 103 | */ 104 | public function isOptional() 105 | { 106 | return $this->optional; 107 | } 108 | } 109 | 110 | /** 111 | * Represents a PHP requirement in form of a php.ini configuration. 112 | * 113 | * @author Tobias Schultze 114 | */ 115 | class PhpIniRequirement extends Requirement 116 | { 117 | /** 118 | * Constructor that initializes the requirement. 119 | * 120 | * @param string $cfgName The configuration name used for ini_get() 121 | * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, 122 | or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement 123 | * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. 124 | This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. 125 | Example: You require a config to be true but PHP later removes this config and defaults it to true internally. 126 | * @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) 127 | * @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) 128 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 129 | * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement 130 | */ 131 | public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false) 132 | { 133 | $cfgValue = ini_get($cfgName); 134 | 135 | if (is_callable($evaluation)) { 136 | if (null === $testMessage || null === $helpHtml) { 137 | throw new InvalidArgumentException('You must provide the parameters testMessage and helpHtml for a callback evaluation.'); 138 | } 139 | 140 | $fulfilled = call_user_func($evaluation, $cfgValue); 141 | } else { 142 | if (null === $testMessage) { 143 | $testMessage = sprintf('%s %s be %s in php.ini', 144 | $cfgName, 145 | $optional ? 'should' : 'must', 146 | $evaluation ? 'enabled' : 'disabled' 147 | ); 148 | } 149 | 150 | if (null === $helpHtml) { 151 | $helpHtml = sprintf('Set %s to %s in php.ini*.', 152 | $cfgName, 153 | $evaluation ? 'on' : 'off' 154 | ); 155 | } 156 | 157 | $fulfilled = $evaluation == $cfgValue; 158 | } 159 | 160 | parent::__construct($fulfilled || ($approveCfgAbsence && false === $cfgValue), $testMessage, $helpHtml, $helpText, $optional); 161 | } 162 | } 163 | 164 | /** 165 | * A RequirementCollection represents a set of Requirement instances. 166 | * 167 | * @author Tobias Schultze 168 | */ 169 | class RequirementCollection implements IteratorAggregate 170 | { 171 | private $requirements = array(); 172 | 173 | /** 174 | * Gets the current RequirementCollection as an Iterator. 175 | * 176 | * @return Traversable A Traversable interface 177 | */ 178 | public function getIterator() 179 | { 180 | return new ArrayIterator($this->requirements); 181 | } 182 | 183 | /** 184 | * Adds a Requirement. 185 | * 186 | * @param Requirement $requirement A Requirement instance 187 | */ 188 | public function add(Requirement $requirement) 189 | { 190 | $this->requirements[] = $requirement; 191 | } 192 | 193 | /** 194 | * Adds a mandatory requirement. 195 | * 196 | * @param bool $fulfilled Whether the requirement is fulfilled 197 | * @param string $testMessage The message for testing the requirement 198 | * @param string $helpHtml The help text formatted in HTML for resolving the problem 199 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 200 | */ 201 | public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null) 202 | { 203 | $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, false)); 204 | } 205 | 206 | /** 207 | * Adds an optional recommendation. 208 | * 209 | * @param bool $fulfilled Whether the recommendation is fulfilled 210 | * @param string $testMessage The message for testing the recommendation 211 | * @param string $helpHtml The help text formatted in HTML for resolving the problem 212 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 213 | */ 214 | public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null) 215 | { 216 | $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, true)); 217 | } 218 | 219 | /** 220 | * Adds a mandatory requirement in form of a php.ini configuration. 221 | * 222 | * @param string $cfgName The configuration name used for ini_get() 223 | * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, 224 | or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement 225 | * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. 226 | This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. 227 | Example: You require a config to be true but PHP later removes this config and defaults it to true internally. 228 | * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) 229 | * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) 230 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 231 | */ 232 | public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) 233 | { 234 | $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, false)); 235 | } 236 | 237 | /** 238 | * Adds an optional recommendation in form of a php.ini configuration. 239 | * 240 | * @param string $cfgName The configuration name used for ini_get() 241 | * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, 242 | or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement 243 | * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. 244 | This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. 245 | Example: You require a config to be true but PHP later removes this config and defaults it to true internally. 246 | * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) 247 | * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) 248 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 249 | */ 250 | public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) 251 | { 252 | $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, true)); 253 | } 254 | 255 | /** 256 | * Adds a requirement collection to the current set of requirements. 257 | * 258 | * @param RequirementCollection $collection A RequirementCollection instance 259 | */ 260 | public function addCollection(RequirementCollection $collection) 261 | { 262 | $this->requirements = array_merge($this->requirements, $collection->all()); 263 | } 264 | 265 | /** 266 | * Returns both requirements and recommendations. 267 | * 268 | * @return array Array of Requirement instances 269 | */ 270 | public function all() 271 | { 272 | return $this->requirements; 273 | } 274 | 275 | /** 276 | * Returns all mandatory requirements. 277 | * 278 | * @return array Array of Requirement instances 279 | */ 280 | public function getRequirements() 281 | { 282 | $array = array(); 283 | foreach ($this->requirements as $req) { 284 | if (!$req->isOptional()) { 285 | $array[] = $req; 286 | } 287 | } 288 | 289 | return $array; 290 | } 291 | 292 | /** 293 | * Returns the mandatory requirements that were not met. 294 | * 295 | * @return array Array of Requirement instances 296 | */ 297 | public function getFailedRequirements() 298 | { 299 | $array = array(); 300 | foreach ($this->requirements as $req) { 301 | if (!$req->isFulfilled() && !$req->isOptional()) { 302 | $array[] = $req; 303 | } 304 | } 305 | 306 | return $array; 307 | } 308 | 309 | /** 310 | * Returns all optional recommendations. 311 | * 312 | * @return array Array of Requirement instances 313 | */ 314 | public function getRecommendations() 315 | { 316 | $array = array(); 317 | foreach ($this->requirements as $req) { 318 | if ($req->isOptional()) { 319 | $array[] = $req; 320 | } 321 | } 322 | 323 | return $array; 324 | } 325 | 326 | /** 327 | * Returns the recommendations that were not met. 328 | * 329 | * @return array Array of Requirement instances 330 | */ 331 | public function getFailedRecommendations() 332 | { 333 | $array = array(); 334 | foreach ($this->requirements as $req) { 335 | if (!$req->isFulfilled() && $req->isOptional()) { 336 | $array[] = $req; 337 | } 338 | } 339 | 340 | return $array; 341 | } 342 | 343 | /** 344 | * Returns whether a php.ini configuration is not correct. 345 | * 346 | * @return bool php.ini configuration problem? 347 | */ 348 | public function hasPhpIniConfigIssue() 349 | { 350 | foreach ($this->requirements as $req) { 351 | if (!$req->isFulfilled() && $req instanceof PhpIniRequirement) { 352 | return true; 353 | } 354 | } 355 | 356 | return false; 357 | } 358 | 359 | /** 360 | * Returns the PHP configuration file (php.ini) path. 361 | * 362 | * @return string|false php.ini file path 363 | */ 364 | public function getPhpIniConfigPath() 365 | { 366 | return get_cfg_var('cfg_file_path'); 367 | } 368 | } 369 | 370 | /** 371 | * This class specifies all requirements and optional recommendations that 372 | * are necessary to run the Symfony Standard Edition. 373 | * 374 | * @author Tobias Schultze 375 | * @author Fabien Potencier 376 | */ 377 | class SymfonyRequirements extends RequirementCollection 378 | { 379 | const REQUIRED_PHP_VERSION = '5.3.3'; 380 | 381 | /** 382 | * Constructor that initializes the requirements. 383 | */ 384 | public function __construct() 385 | { 386 | /* mandatory requirements follow */ 387 | 388 | $installedPhpVersion = phpversion(); 389 | 390 | $this->addRequirement( 391 | version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>='), 392 | sprintf('PHP version must be at least %s (%s installed)', self::REQUIRED_PHP_VERSION, $installedPhpVersion), 393 | sprintf('You are running PHP version "%s", but Symfony needs at least PHP "%s" to run. 394 | Before using Symfony, upgrade your PHP installation, preferably to the latest version.', 395 | $installedPhpVersion, self::REQUIRED_PHP_VERSION), 396 | sprintf('Install PHP %s or newer (installed version is %s)', self::REQUIRED_PHP_VERSION, $installedPhpVersion) 397 | ); 398 | 399 | $this->addRequirement( 400 | version_compare($installedPhpVersion, '5.3.16', '!='), 401 | 'PHP version must not be 5.3.16 as Symfony won\'t work properly with it', 402 | 'Install PHP 5.3.17 or newer (or downgrade to an earlier PHP version)' 403 | ); 404 | 405 | $this->addRequirement( 406 | is_dir(__DIR__.'/../vendor/composer'), 407 | 'Vendor libraries must be installed', 408 | 'Vendor libraries are missing. Install composer following instructions from http://getcomposer.org/. '. 409 | 'Then run "php composer.phar install" to install them.' 410 | ); 411 | 412 | $cacheDir = is_dir(__DIR__.'/../var/cache') ? __DIR__.'/../var/cache' : __DIR__.'/cache'; 413 | 414 | $this->addRequirement( 415 | is_writable($cacheDir), 416 | 'app/cache/ or var/cache/ directory must be writable', 417 | 'Change the permissions of either "app/cache/" or "var/cache/" directory so that the web server can write into it.' 418 | ); 419 | 420 | $logsDir = is_dir(__DIR__.'/../var/logs') ? __DIR__.'/../var/logs' : __DIR__.'/logs'; 421 | 422 | $this->addRequirement( 423 | is_writable($logsDir), 424 | 'app/logs/ or var/logs/ directory must be writable', 425 | 'Change the permissions of either "app/logs/" or "var/logs/" directory so that the web server can write into it.' 426 | ); 427 | 428 | $this->addPhpIniRequirement( 429 | 'date.timezone', true, false, 430 | 'date.timezone setting must be set', 431 | 'Set the "date.timezone" setting in php.ini* (like Europe/Paris).' 432 | ); 433 | 434 | if (version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>=')) { 435 | $timezones = array(); 436 | foreach (DateTimeZone::listAbbreviations() as $abbreviations) { 437 | foreach ($abbreviations as $abbreviation) { 438 | $timezones[$abbreviation['timezone_id']] = true; 439 | } 440 | } 441 | 442 | $this->addRequirement( 443 | isset($timezones[@date_default_timezone_get()]), 444 | sprintf('Configured default timezone "%s" must be supported by your installation of PHP', @date_default_timezone_get()), 445 | 'Your default timezone is not supported by PHP. Check for typos in your php.ini file and have a look at the list of deprecated timezones at http://php.net/manual/en/timezones.others.php.' 446 | ); 447 | } 448 | 449 | $this->addRequirement( 450 | function_exists('json_encode'), 451 | 'json_encode() must be available', 452 | 'Install and enable the JSON extension.' 453 | ); 454 | 455 | $this->addRequirement( 456 | function_exists('session_start'), 457 | 'session_start() must be available', 458 | 'Install and enable the session extension.' 459 | ); 460 | 461 | $this->addRequirement( 462 | function_exists('ctype_alpha'), 463 | 'ctype_alpha() must be available', 464 | 'Install and enable the ctype extension.' 465 | ); 466 | 467 | $this->addRequirement( 468 | function_exists('token_get_all'), 469 | 'token_get_all() must be available', 470 | 'Install and enable the Tokenizer extension.' 471 | ); 472 | 473 | $this->addRequirement( 474 | function_exists('simplexml_import_dom'), 475 | 'simplexml_import_dom() must be available', 476 | 'Install and enable the SimpleXML extension.' 477 | ); 478 | 479 | if (function_exists('apc_store') && ini_get('apc.enabled')) { 480 | if (version_compare($installedPhpVersion, '5.4.0', '>=')) { 481 | $this->addRequirement( 482 | version_compare(phpversion('apc'), '3.1.13', '>='), 483 | 'APC version must be at least 3.1.13 when using PHP 5.4', 484 | 'Upgrade your APC extension (3.1.13+).' 485 | ); 486 | } else { 487 | $this->addRequirement( 488 | version_compare(phpversion('apc'), '3.0.17', '>='), 489 | 'APC version must be at least 3.0.17', 490 | 'Upgrade your APC extension (3.0.17+).' 491 | ); 492 | } 493 | } 494 | 495 | $this->addPhpIniRequirement('detect_unicode', false); 496 | 497 | if (extension_loaded('suhosin')) { 498 | $this->addPhpIniRequirement( 499 | 'suhosin.executor.include.whitelist', 500 | create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'), 501 | false, 502 | 'suhosin.executor.include.whitelist must be configured correctly in php.ini', 503 | 'Add "phar" to suhosin.executor.include.whitelist in php.ini*.' 504 | ); 505 | } 506 | 507 | if (extension_loaded('xdebug')) { 508 | $this->addPhpIniRequirement( 509 | 'xdebug.show_exception_trace', false, true 510 | ); 511 | 512 | $this->addPhpIniRequirement( 513 | 'xdebug.scream', false, true 514 | ); 515 | 516 | $this->addPhpIniRecommendation( 517 | 'xdebug.max_nesting_level', 518 | create_function('$cfgValue', 'return $cfgValue > 100;'), 519 | true, 520 | 'xdebug.max_nesting_level should be above 100 in php.ini', 521 | 'Set "xdebug.max_nesting_level" to e.g. "250" in php.ini* to stop Xdebug\'s infinite recursion protection erroneously throwing a fatal error in your project.' 522 | ); 523 | } 524 | 525 | $pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null; 526 | 527 | $this->addRequirement( 528 | null !== $pcreVersion, 529 | 'PCRE extension must be available', 530 | 'Install the PCRE extension (version 8.0+).' 531 | ); 532 | 533 | if (extension_loaded('mbstring')) { 534 | $this->addPhpIniRequirement( 535 | 'mbstring.func_overload', 536 | create_function('$cfgValue', 'return (int) $cfgValue === 0;'), 537 | true, 538 | 'string functions should not be overloaded', 539 | 'Set "mbstring.func_overload" to 0 in php.ini* to disable function overloading by the mbstring extension.' 540 | ); 541 | } 542 | 543 | /* optional recommendations follow */ 544 | 545 | $this->addRecommendation( 546 | file_get_contents(__FILE__) === file_get_contents(__DIR__.'/../vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/SymfonyRequirements.php'), 547 | 'Requirements file should be up-to-date', 548 | 'Your requirements file is outdated. Run composer install and re-check your configuration.' 549 | ); 550 | 551 | $this->addRecommendation( 552 | version_compare($installedPhpVersion, '5.3.4', '>='), 553 | 'You should use at least PHP 5.3.4 due to PHP bug #52083 in earlier versions', 554 | 'Your project might malfunction randomly due to PHP bug #52083 ("Notice: Trying to get property of non-object"). Install PHP 5.3.4 or newer.' 555 | ); 556 | 557 | $this->addRecommendation( 558 | version_compare($installedPhpVersion, '5.3.8', '>='), 559 | 'When using annotations you should have at least PHP 5.3.8 due to PHP bug #55156', 560 | 'Install PHP 5.3.8 or newer if your project uses annotations.' 561 | ); 562 | 563 | $this->addRecommendation( 564 | version_compare($installedPhpVersion, '5.4.0', '!='), 565 | 'You should not use PHP 5.4.0 due to the PHP bug #61453', 566 | 'Your project might not work properly due to the PHP bug #61453 ("Cannot dump definitions which have method calls"). Install PHP 5.4.1 or newer.' 567 | ); 568 | 569 | $this->addRecommendation( 570 | version_compare($installedPhpVersion, '5.4.11', '>='), 571 | 'When using the logout handler from the Symfony Security Component, you should have at least PHP 5.4.11 due to PHP bug #63379 (as a workaround, you can also set invalidate_session to false in the security logout handler configuration)', 572 | 'Install PHP 5.4.11 or newer if your project uses the logout handler from the Symfony Security Component.' 573 | ); 574 | 575 | $this->addRecommendation( 576 | (version_compare($installedPhpVersion, '5.3.18', '>=') && version_compare($installedPhpVersion, '5.4.0', '<')) 577 | || 578 | version_compare($installedPhpVersion, '5.4.8', '>='), 579 | 'You should use PHP 5.3.18+ or PHP 5.4.8+ to always get nice error messages for fatal errors in the development environment due to PHP bug #61767/#60909', 580 | 'Install PHP 5.3.18+ or PHP 5.4.8+ if you want nice error messages for all fatal errors in the development environment.' 581 | ); 582 | 583 | if (null !== $pcreVersion) { 584 | $this->addRecommendation( 585 | $pcreVersion >= 8.0, 586 | sprintf('PCRE extension should be at least version 8.0 (%s installed)', $pcreVersion), 587 | 'PCRE 8.0+ is preconfigured in PHP since 5.3.2 but you are using an outdated version of it. Symfony probably works anyway but it is recommended to upgrade your PCRE extension.' 588 | ); 589 | } 590 | 591 | $this->addRecommendation( 592 | class_exists('DomDocument'), 593 | 'PHP-DOM and PHP-XML modules should be installed', 594 | 'Install and enable the PHP-DOM and the PHP-XML modules.' 595 | ); 596 | 597 | $this->addRecommendation( 598 | function_exists('mb_strlen'), 599 | 'mb_strlen() should be available', 600 | 'Install and enable the mbstring extension.' 601 | ); 602 | 603 | $this->addRecommendation( 604 | function_exists('iconv'), 605 | 'iconv() should be available', 606 | 'Install and enable the iconv extension.' 607 | ); 608 | 609 | $this->addRecommendation( 610 | function_exists('utf8_decode'), 611 | 'utf8_decode() should be available', 612 | 'Install and enable the XML extension.' 613 | ); 614 | 615 | $this->addRecommendation( 616 | function_exists('filter_var'), 617 | 'filter_var() should be available', 618 | 'Install and enable the filter extension.' 619 | ); 620 | 621 | if (!defined('PHP_WINDOWS_VERSION_BUILD')) { 622 | $this->addRecommendation( 623 | function_exists('posix_isatty'), 624 | 'posix_isatty() should be available', 625 | 'Install and enable the php_posix extension (used to colorize the CLI output).' 626 | ); 627 | } 628 | 629 | $this->addRecommendation( 630 | class_exists('Locale'), 631 | 'intl extension should be available', 632 | 'Install and enable the intl extension (used for validators).' 633 | ); 634 | 635 | if (class_exists('Collator')) { 636 | $this->addRecommendation( 637 | null !== new Collator('fr_FR'), 638 | 'intl extension should be correctly configured', 639 | 'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.' 640 | ); 641 | } 642 | 643 | if (class_exists('Locale')) { 644 | if (defined('INTL_ICU_VERSION')) { 645 | $version = INTL_ICU_VERSION; 646 | } else { 647 | $reflector = new ReflectionExtension('intl'); 648 | 649 | ob_start(); 650 | $reflector->info(); 651 | $output = strip_tags(ob_get_clean()); 652 | 653 | preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches); 654 | $version = $matches[1]; 655 | } 656 | 657 | $this->addRecommendation( 658 | version_compare($version, '4.0', '>='), 659 | 'intl ICU version should be at least 4+', 660 | 'Upgrade your intl extension with a newer ICU version (4+).' 661 | ); 662 | } 663 | 664 | $accelerator = 665 | (extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) 666 | || 667 | (extension_loaded('apc') && ini_get('apc.enabled')) 668 | || 669 | (extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.enable')) 670 | || 671 | (extension_loaded('Zend OPcache') && ini_get('opcache.enable')) 672 | || 673 | (extension_loaded('xcache') && ini_get('xcache.cacher')) 674 | || 675 | (extension_loaded('wincache') && ini_get('wincache.ocenabled')) 676 | ; 677 | 678 | $this->addRecommendation( 679 | $accelerator, 680 | 'a PHP accelerator should be installed', 681 | 'Install and/or enable a PHP accelerator (highly recommended).' 682 | ); 683 | 684 | if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 685 | $this->addRecommendation( 686 | $this->getRealpathCacheSize() > 1000, 687 | 'realpath_cache_size should be above 1024 in php.ini', 688 | 'Set "realpath_cache_size" to e.g. "1024" in php.ini* to improve performance on windows.' 689 | ); 690 | } 691 | 692 | $this->addPhpIniRecommendation('short_open_tag', false); 693 | 694 | $this->addPhpIniRecommendation('magic_quotes_gpc', false, true); 695 | 696 | $this->addPhpIniRecommendation('register_globals', false, true); 697 | 698 | $this->addPhpIniRecommendation('session.auto_start', false); 699 | 700 | $this->addRecommendation( 701 | class_exists('PDO'), 702 | 'PDO should be installed', 703 | 'Install PDO (mandatory for Doctrine).' 704 | ); 705 | 706 | if (class_exists('PDO')) { 707 | $drivers = PDO::getAvailableDrivers(); 708 | $this->addRecommendation( 709 | count($drivers) > 0, 710 | sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'), 711 | 'Install PDO drivers (mandatory for Doctrine).' 712 | ); 713 | } 714 | } 715 | 716 | /** 717 | * Loads realpath_cache_size from php.ini and converts it to int. 718 | * 719 | * (e.g. 16k is converted to 16384 int) 720 | * 721 | * @return int 722 | */ 723 | protected function getRealpathCacheSize() 724 | { 725 | $size = ini_get('realpath_cache_size'); 726 | $size = trim($size); 727 | $unit = strtolower(substr($size, -1, 1)); 728 | switch ($unit) { 729 | case 'g': 730 | return $size * 1024 * 1024 * 1024; 731 | case 'm': 732 | return $size * 1024 * 1024; 733 | case 'k': 734 | return $size * 1024; 735 | default: 736 | return (int) $size; 737 | } 738 | } 739 | } 740 | --------------------------------------------------------------------------------