├── .gitignore ├── LICENSE ├── README.md ├── UPGRADE-2.2.md ├── UPGRADE-2.3.md ├── UPGRADE-2.4.md ├── UPGRADE.md ├── app ├── .htaccess ├── AppCache.php ├── AppKernel.php ├── Resources │ └── views │ │ └── base.html.twig ├── SymfonyRequirements.php ├── autoload.php ├── check.php ├── config │ ├── config.yml │ ├── config_codecept_test.yml │ ├── config_dev.yml │ ├── config_prod.yml │ ├── config_test.yml │ ├── parameters.yml.dist │ ├── routing.yml │ ├── routing_dev.yml │ └── security.yml ├── console └── phpunit.xml.dist ├── bin ├── codecept ├── doctrine ├── doctrine.php ├── phpunit └── security-checker ├── codeception.yml ├── composer.json ├── composer.lock ├── src └── CodeReview │ └── RestBundle │ ├── CodeReviewRestBundle.php │ ├── Controller │ └── ArtistController.php │ ├── DataFixtures │ └── ORM │ │ └── LoadArtistData.php │ ├── DependencyInjection │ ├── CodeReviewRestExtension.php │ └── Configuration.php │ ├── Entity │ └── Artist.php │ ├── Exception │ └── InvalidFormException.php │ ├── Form │ ├── Handler │ │ └── FormHandler.php │ └── Type │ │ └── ArtistType.php │ ├── Handler │ ├── ArtistHandler.php │ └── HandlerInterface.php │ ├── Model │ └── ArtistInterface.php │ ├── Repository │ └── ArtistRepository.php │ ├── Resources │ └── config │ │ ├── routing.yml │ │ └── services.yml │ └── Tests │ └── Controller │ └── DefaultControllerTest.php ├── tests ├── _bootstrap.php ├── _data │ └── dump.sql ├── _helpers │ ├── ApiHelper.php │ ├── CodeHelper.php │ ├── TestHelper.php │ └── WebHelper.php ├── _log │ └── .gitignore ├── _pages │ ├── ApiArtistPage.php │ └── ApiBasePage.php ├── acceptance.suite.yml ├── acceptance │ ├── WebGuy.php │ └── _bootstrap.php ├── api.suite.yml ├── api │ ├── ApiGuy.php │ ├── CodeReview │ │ └── RestBundle │ │ │ └── Controller │ │ │ └── ArtistControllerCest.php │ └── _bootstrap.php ├── functional.suite.yml ├── functional │ ├── TestGuy.php │ └── _bootstrap.php ├── unit.suite.yml └── unit │ ├── CodeGuy.php │ ├── CodeReview │ └── RestBundle │ │ ├── Form │ │ ├── Handler │ │ │ └── FormHandlerTest.php │ │ └── Type │ │ │ └── ArtistFormTest.php │ │ └── Handler │ │ └── HandlerTest.php │ └── _bootstrap.php └── web ├── .htaccess ├── app.php ├── app_codecept_test.php ├── app_dev.php ├── apple-touch-icon.png ├── config.php ├── favicon.ico └── robots.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ / PhpStorm 2 | .idea/* 3 | *.ipr 4 | *.iml 5 | *.iws 6 | 7 | # Bootstrap 8 | app/bootstrap* 9 | 10 | # Symfony directories 11 | vendor/* 12 | */logs/* 13 | */cache/* 14 | web/uploads/* 15 | web/bundles/* 16 | 17 | # Configuration files 18 | app/config/parameters.ini 19 | app/config/parameters.yml 20 | 21 | # Composer 22 | composer.phar -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2004-2014 Fabien Potencier 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 furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | 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. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is the code that is used in the [How to code a Symfony 2 RESTful API with FOS REST Bundle][1] video series at [CodeReviewVideos.com][2] 2 | 3 | In this course you will learn how to set up, configure, and implement a RESTful API using FOS REST Bundle. 4 | 5 | We will cover all the basics including GET, POST, PUT, and DELETE, along with handling collections of related data, how to leverage Symfony's awesome Forms component in a way you may never have thought of, and importantly - we will do all of this in a Test Driven Development manner. 6 | 7 | By the end of this course you will be confident in implementing your own REST API using Symfony 2. And once you have that, you'll see how many possibilities become available to you - mobile apps, awesome AngularJS front-ends, and many more opportunities besides. 8 | 9 | ### Code For Individual Videos 10 | 11 | **To see the code that matches up to the videos**, take a look at the [commit history][3] - the video names should match up. I know this isn't great, but it may help you with specific issues to see the code as it was at the times the videos were recorded. 12 | 13 | It's good stuff, so let's get cracking. 14 | 15 | [1]: https://www.codereviewvideos.com/course/how-to-code-a-symfony-2-restful-api-with-fos-rest-bundle 16 | 17 | [2]: https://www.codereviewvideos.com/ 18 | 19 | [3]: https://github.com/a6software/symfony2-rest-example/commits/master 20 | -------------------------------------------------------------------------------- /UPGRADE-2.2.md: -------------------------------------------------------------------------------- 1 | UPGRADE FROM 2.1 to 2.2 2 | ======================= 3 | 4 | * The [`web/.htaccess`](https://github.com/symfony/symfony-standard/blob/2.2/web/.htaccess) 5 | file has been enhanced substantially to prevent duplicate content with and 6 | without `/app.php` in the URI. It also improves functionality when using 7 | Apache aliases or when mod_rewrite is not available. So you might want to 8 | update your `.htaccess` file as well. 9 | 10 | * The ``_internal`` route is not used any more. It should then be removed 11 | from both your routing and security configurations. A ``fragments`` key has 12 | been added to the framework configuration and must be specified when ESI or 13 | Hinclude are in use. No security configuration is required for this path as 14 | by default ESI access is only permitted for trusted hosts and Hinclude 15 | access uses an URL signing mechanism. 16 | 17 | ``` 18 | framework: 19 | # ... 20 | fragments: { path: /_proxy } 21 | ``` 22 | 23 | Functional Tests 24 | ---------------- 25 | 26 | * The profiler has been disabled by default in the test environment. You can 27 | enable it again by modifying the ``config_test.yml`` configuration file or 28 | even better, you can just enable it for the very next request by calling 29 | ``$client->enableProfiler()`` when you need the profiler in a test (that 30 | speeds up functional tests quite a bit). 31 | -------------------------------------------------------------------------------- /UPGRADE-2.3.md: -------------------------------------------------------------------------------- 1 | UPGRADE FROM 2.2 to 2.3 2 | ======================= 3 | 4 | When upgrading Symfony from 2.2 to 2.3, you need to do the following changes 5 | to the code that came from the Standard Edition: 6 | 7 | * The debugging tools are not enabled by default anymore and should be added 8 | to the 9 | [`web/app_dev.php`](https://github.com/symfony/symfony-standard/blob/2.3/web/app_dev.php) 10 | front controller manually, just after including the bootstrap cache: 11 | 12 | use Symfony\Component\Debug\Debug; 13 | 14 | Debug::enable(); 15 | 16 | You also need to enable debugging in the 17 | [`app/console`](https://github.com/symfony/symfony-standard/blob/2.3/app/console) 18 | script, after the `$debug` variable is defined: 19 | 20 | use Symfony\Component\Debug\Debug; 21 | 22 | if ($debug) { 23 | Debug::enable(); 24 | } 25 | 26 | * The `parameters.yml` file can now be managed by the 27 | `incenteev/composer-parameter-handler` bundle that comes with the 2.3 28 | Standard Edition: 29 | 30 | * add `"incenteev/composer-parameter-handler": "~2.0"` to your 31 | `composer.json` file; 32 | 33 | * add `/app/config/parameters.yml` to your `.gitignore` file; 34 | 35 | * create a 36 | [`app/config/parameters.yml.dist`](https://github.com/symfony/symfony-standard/blob/2.3/app/config/parameters.yml.dist) 37 | file with sensible values for all your parameters. 38 | 39 | * It is highly recommended that you switch the minimum stability to `stable` 40 | in your `composer.json` file. 41 | 42 | * If you are using Apache, have a look at the new 43 | [`.htaccess`](https://github.com/symfony/symfony-standard/blob/2.3/web/.htaccess) 44 | configuration and change yours accordingly. 45 | 46 | * In the 47 | [`app/autoload.php`](https://github.com/symfony/symfony-standard/blob/2.3/app/autoload.php) 48 | file, the section about `intl` should be removed as it is not needed anymore. 49 | 50 | You can also have a look at the 51 | [diff](https://github.com/symfony/symfony-standard/compare/v2.2.0%E2%80%A62.3) 52 | between the 2.2 version of the Standard Edition and the 2.3 version. 53 | -------------------------------------------------------------------------------- /UPGRADE-2.4.md: -------------------------------------------------------------------------------- 1 | UPGRADE FROM 2.3 to 2.4 2 | ======================= 3 | 4 | When upgrading Symfony from 2.3 to 2.4, you need to do the following changes 5 | to the code that came from the Standard Edition: 6 | 7 | * We recommend to comment or remove the `firephp` and `chromephp` Monolog 8 | handlers as they might cause issues with some configuration (`chromephp` 9 | with Nginx for instance). 10 | -------------------------------------------------------------------------------- /UPGRADE.md: -------------------------------------------------------------------------------- 1 | Symfony Standard Edition Upgrade 2 | ================================ 3 | 4 | From Symfony 2.0 to Symfony 2.1 5 | ------------------------------- 6 | 7 | ### Project Dependencies 8 | 9 | As of Symfony 2.1, project dependencies are managed by 10 | [Composer](http://getcomposer.org/): 11 | 12 | * The `bin/vendors` script can be removed as `composer.phar` does all the work 13 | now (it is recommended to install it globally on your machine). 14 | 15 | * The `deps` file need to be replaced with the `composer.json` one. 16 | 17 | * The `composer.lock` is the equivalent of the generated `deps.lock` file and 18 | it is automatically generated by Composer. 19 | 20 | Download the default 21 | [`composer.json`](https://raw.github.com/symfony/symfony-standard/2.1/composer.json) 22 | and 23 | [`composer.lock`](https://raw.github.com/symfony/symfony-standard/2.1/composer.lock) 24 | files for Symfony 2.1 and put them into the main directory of your project. If 25 | you have customized your `deps` file, move the added dependencies to the 26 | `composer.json` file (many bundles and PHP libraries are already available as 27 | Composer packages -- search for them on [Packagist](http://packagist.org/)). 28 | 29 | Remove your current `vendor` directory. 30 | 31 | Finally, run Composer: 32 | 33 | $ composer.phar install 34 | 35 | Note: You must complete the upgrade steps below so composer can successfully generate the autoload files. 36 | 37 | ### `app/autoload.php` 38 | 39 | The default `autoload.php` reads as follows (it has been simplified a lot as 40 | autoloading for libraries and bundles declared in your `composer.json` file is 41 | automatically managed by the Composer autoloader): 42 | 43 | add('', __DIR__.'/../vendor/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs'); 54 | } 55 | 56 | AnnotationRegistry::registerLoader(array($loader, 'loadClass')); 57 | 58 | return $loader; 59 | 60 | ### `app/config/config.yml` 61 | 62 | The `framework.charset` setting must be removed. If you are not using `UTF-8` 63 | for your application, override the `getCharset()` method in your `AppKernel` 64 | class instead: 65 | 66 | class AppKernel extends Kernel 67 | { 68 | public function getCharset() 69 | { 70 | return 'ISO-8859-1'; 71 | } 72 | 73 | // ... 74 | } 75 | 76 | You might want to add the new `strict_requirements` parameter to 77 | `framework.router` (it avoids fatal errors in the production environment when 78 | a link cannot be generated): 79 | 80 | framework: 81 | router: 82 | strict_requirements: "%kernel.debug%" 83 | 84 | You can even disable the requirements check on production with `null` as you should 85 | know that the parameters for URL generation always pass the requirements, e.g. by 86 | validating them beforehand. This additionally enhances performance. See 87 | [config_prod.yml](https://github.com/symfony/symfony-standard/blob/master/app/config/config_prod.yml). 88 | 89 | The `default_locale` parameter is now a setting of the main `framework` 90 | configuration (it was under the `framework.session` in 2.0): 91 | 92 | framework: 93 | default_locale: "%locale%" 94 | 95 | The `auto_start` setting under `framework.session` must be removed as it is 96 | not used anymore (the session is now always started on-demand). If 97 | `auto_start` was the only setting under the `framework.session` entry, don't 98 | remove it entirely, but set its value to `~` (`~` means `null` in YAML) 99 | instead: 100 | 101 | framework: 102 | session: ~ 103 | 104 | The `trust_proxy_headers` setting was added in the default configuration file 105 | (as it should be set to `true` when you install your application behind a 106 | reverse proxy): 107 | 108 | framework: 109 | trust_proxy_headers: false 110 | 111 | An empty `bundles` entry was added to the `assetic` configuration: 112 | 113 | assetic: 114 | bundles: [] 115 | 116 | The default `swiftmailer` configuration now has the `spool` setting configured 117 | to the `memory` type to defer email sending after the response is sent to the 118 | user (recommended for better end-user performance): 119 | 120 | swiftmailer: 121 | spool: { type: memory } 122 | 123 | The `jms_security_extra` configuration was moved to the `security.yml` 124 | configuration file. 125 | 126 | ### `app/config/config_dev.yml` 127 | 128 | An example of how to send all emails to a unique address was added: 129 | 130 | #swiftmailer: 131 | # delivery_address: me@example.com 132 | 133 | ### `app/config/config_test.yml` 134 | 135 | The `storage_id` setting must be changed to `session.storage.mock_file`: 136 | 137 | framework: 138 | session: 139 | storage_id: session.storage.mock_file 140 | 141 | ### `app/config/parameters.ini` 142 | 143 | The file has been converted to a YAML file which reads as follows: 144 | 145 | parameters: 146 | database_driver: pdo_mysql 147 | database_host: localhost 148 | database_port: ~ 149 | database_name: symfony 150 | database_user: root 151 | database_password: ~ 152 | 153 | mailer_transport: smtp 154 | mailer_host: localhost 155 | mailer_user: ~ 156 | mailer_password: ~ 157 | 158 | locale: en 159 | secret: ThisTokenIsNotSoSecretChangeIt 160 | 161 | Note that if you convert your parameters file to YAML, you must also change 162 | its reference in `app/config/config.yml`. 163 | 164 | ### `app/config/routing_dev.yml` 165 | 166 | The `_assetic` entry was removed: 167 | 168 | #_assetic: 169 | # resource: . 170 | # type: assetic 171 | 172 | ### `app/config/security.yml` 173 | 174 | Under `security.access_control`, the default rule for internal routes was changed: 175 | 176 | security: 177 | access_control: 178 | #- { path: ^/_internal/secure, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 } 179 | 180 | Under `security.providers`, the `in_memory` example was updated to the following: 181 | 182 | security: 183 | providers: 184 | in_memory: 185 | memory: 186 | users: 187 | user: { password: userpass, roles: [ 'ROLE_USER' ] } 188 | admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] } 189 | 190 | ### `app/AppKernel.php` 191 | 192 | The following bundles have been added to the list of default registered bundles: 193 | 194 | new JMS\AopBundle\JMSAopBundle(), 195 | new JMS\DiExtraBundle\JMSDiExtraBundle($this), 196 | 197 | You must also rename the DoctrineBundle from: 198 | 199 | new Symfony\Bundle\DoctrineBundle\DoctrineBundle(), 200 | 201 | to: 202 | 203 | new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), 204 | 205 | ### `web/app.php` 206 | 207 | The default `web/app.php` file now reads as follows: 208 | 209 | register(true); 222 | */ 223 | 224 | require_once __DIR__.'/../app/AppKernel.php'; 225 | //require_once __DIR__.'/../app/AppCache.php'; 226 | 227 | $kernel = new AppKernel('prod', false); 228 | $kernel->loadClassCache(); 229 | //$kernel = new AppCache($kernel); 230 | $request = Request::createFromGlobals(); 231 | $response = $kernel->handle($request); 232 | $response->send(); 233 | $kernel->terminate($request, $response); 234 | 235 | ### `web/app_dev.php` 236 | 237 | The default `web/app_dev.php` file now reads as follows: 238 | 239 | loadClassCache(); 265 | $request = Request::createFromGlobals(); 266 | $response = $kernel->handle($request); 267 | $response->send(); 268 | $kernel->terminate($request, $response); 269 | -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Require all denied 3 | 4 | 5 | Order deny,allow 6 | Deny from all 7 | 8 | -------------------------------------------------------------------------------- /app/AppCache.php: -------------------------------------------------------------------------------- 1 | getEnvironment(), array('dev', 'test'))) { 31 | $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); 32 | $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); 33 | $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); 34 | } 35 | 36 | return $bundles; 37 | } 38 | 39 | public function registerContainerConfiguration(LoaderInterface $loader) 40 | { 41 | $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/Resources/views/base.html.twig: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /app/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 | /* optional recommendations follow */ 534 | 535 | $this->addRecommendation( 536 | file_get_contents(__FILE__) === file_get_contents(__DIR__.'/../vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/SymfonyRequirements.php'), 537 | 'Requirements file should be up-to-date', 538 | 'Your requirements file is outdated. Run composer install and re-check your configuration.' 539 | ); 540 | 541 | $this->addRecommendation( 542 | version_compare($installedPhpVersion, '5.3.4', '>='), 543 | 'You should use at least PHP 5.3.4 due to PHP bug #52083 in earlier versions', 544 | '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.' 545 | ); 546 | 547 | $this->addRecommendation( 548 | version_compare($installedPhpVersion, '5.3.8', '>='), 549 | 'When using annotations you should have at least PHP 5.3.8 due to PHP bug #55156', 550 | 'Install PHP 5.3.8 or newer if your project uses annotations.' 551 | ); 552 | 553 | $this->addRecommendation( 554 | version_compare($installedPhpVersion, '5.4.0', '!='), 555 | 'You should not use PHP 5.4.0 due to the PHP bug #61453', 556 | '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.' 557 | ); 558 | 559 | $this->addRecommendation( 560 | version_compare($installedPhpVersion, '5.4.11', '>='), 561 | '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)', 562 | 'Install PHP 5.4.11 or newer if your project uses the logout handler from the Symfony Security Component.' 563 | ); 564 | 565 | $this->addRecommendation( 566 | (version_compare($installedPhpVersion, '5.3.18', '>=') && version_compare($installedPhpVersion, '5.4.0', '<')) 567 | || 568 | version_compare($installedPhpVersion, '5.4.8', '>='), 569 | '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', 570 | 'Install PHP 5.3.18+ or PHP 5.4.8+ if you want nice error messages for all fatal errors in the development environment.' 571 | ); 572 | 573 | if (null !== $pcreVersion) { 574 | $this->addRecommendation( 575 | $pcreVersion >= 8.0, 576 | sprintf('PCRE extension should be at least version 8.0 (%s installed)', $pcreVersion), 577 | '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.' 578 | ); 579 | } 580 | 581 | $this->addRecommendation( 582 | class_exists('DomDocument'), 583 | 'PHP-XML module should be installed', 584 | 'Install and enable the PHP-XML module.' 585 | ); 586 | 587 | $this->addRecommendation( 588 | function_exists('mb_strlen'), 589 | 'mb_strlen() should be available', 590 | 'Install and enable the mbstring extension.' 591 | ); 592 | 593 | $this->addRecommendation( 594 | function_exists('iconv'), 595 | 'iconv() should be available', 596 | 'Install and enable the iconv extension.' 597 | ); 598 | 599 | $this->addRecommendation( 600 | function_exists('utf8_decode'), 601 | 'utf8_decode() should be available', 602 | 'Install and enable the XML extension.' 603 | ); 604 | 605 | $this->addRecommendation( 606 | function_exists('filter_var'), 607 | 'filter_var() should be available', 608 | 'Install and enable the filter extension.' 609 | ); 610 | 611 | if (!defined('PHP_WINDOWS_VERSION_BUILD')) { 612 | $this->addRecommendation( 613 | function_exists('posix_isatty'), 614 | 'posix_isatty() should be available', 615 | 'Install and enable the php_posix extension (used to colorize the CLI output).' 616 | ); 617 | } 618 | 619 | $this->addRecommendation( 620 | class_exists('Locale'), 621 | 'intl extension should be available', 622 | 'Install and enable the intl extension (used for validators).' 623 | ); 624 | 625 | if (class_exists('Collator')) { 626 | $this->addRecommendation( 627 | null !== new Collator('fr_FR'), 628 | 'intl extension should be correctly configured', 629 | 'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.' 630 | ); 631 | } 632 | 633 | if (class_exists('Locale')) { 634 | if (defined('INTL_ICU_VERSION')) { 635 | $version = INTL_ICU_VERSION; 636 | } else { 637 | $reflector = new ReflectionExtension('intl'); 638 | 639 | ob_start(); 640 | $reflector->info(); 641 | $output = strip_tags(ob_get_clean()); 642 | 643 | preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches); 644 | $version = $matches[1]; 645 | } 646 | 647 | $this->addRecommendation( 648 | version_compare($version, '4.0', '>='), 649 | 'intl ICU version should be at least 4+', 650 | 'Upgrade your intl extension with a newer ICU version (4+).' 651 | ); 652 | } 653 | 654 | $accelerator = 655 | (extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) 656 | || 657 | (extension_loaded('apc') && ini_get('apc.enabled')) 658 | || 659 | (extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.enable')) 660 | || 661 | (extension_loaded('Zend OPcache') && ini_get('opcache.enable')) 662 | || 663 | (extension_loaded('xcache') && ini_get('xcache.cacher')) 664 | || 665 | (extension_loaded('wincache') && ini_get('wincache.ocenabled')) 666 | ; 667 | 668 | $this->addRecommendation( 669 | $accelerator, 670 | 'a PHP accelerator should be installed', 671 | 'Install and/or enable a PHP accelerator (highly recommended).' 672 | ); 673 | 674 | if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 675 | $this->addPhpIniRecommendation( 676 | 'realpath_cache_size', 677 | create_function('$cfgValue', 'return (int) $cfgValue > 1000;'), 678 | false, 679 | 'realpath_cache_size should be above 1024 in php.ini', 680 | 'Set "realpath_cache_size" to e.g. "1024" in php.ini* to improve performance on windows.' 681 | ); 682 | } 683 | 684 | $this->addPhpIniRecommendation('short_open_tag', false); 685 | 686 | $this->addPhpIniRecommendation('magic_quotes_gpc', false, true); 687 | 688 | $this->addPhpIniRecommendation('register_globals', false, true); 689 | 690 | $this->addPhpIniRecommendation('session.auto_start', false); 691 | 692 | $this->addRecommendation( 693 | class_exists('PDO'), 694 | 'PDO should be installed', 695 | 'Install PDO (mandatory for Doctrine).' 696 | ); 697 | 698 | if (class_exists('PDO')) { 699 | $drivers = PDO::getAvailableDrivers(); 700 | $this->addRecommendation( 701 | count($drivers) > 0, 702 | sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'), 703 | 'Install PDO drivers (mandatory for Doctrine).' 704 | ); 705 | } 706 | } 707 | } 708 | -------------------------------------------------------------------------------- /app/autoload.php: -------------------------------------------------------------------------------- 1 | getPhpIniConfigPath(); 8 | 9 | echo_title('Symfony2 Requirements Checker'); 10 | 11 | echo '> PHP is using the following php.ini file:'.PHP_EOL; 12 | if ($iniPath) { 13 | echo_style('green', ' '.$iniPath); 14 | } else { 15 | echo_style('warning', ' WARNING: No configuration file (php.ini) used by PHP!'); 16 | } 17 | 18 | echo PHP_EOL.PHP_EOL; 19 | 20 | echo '> Checking Symfony requirements:'.PHP_EOL.' '; 21 | 22 | $messages = array(); 23 | foreach ($symfonyRequirements->getRequirements() as $req) { 24 | /** @var $req Requirement */ 25 | if ($helpText = get_error_message($req, $lineSize)) { 26 | echo_style('red', 'E'); 27 | $messages['error'][] = $helpText; 28 | } else { 29 | echo_style('green', '.'); 30 | } 31 | } 32 | 33 | $checkPassed = empty($messages['error']); 34 | 35 | foreach ($symfonyRequirements->getRecommendations() as $req) { 36 | if ($helpText = get_error_message($req, $lineSize)) { 37 | echo_style('yellow', 'W'); 38 | $messages['warning'][] = $helpText; 39 | } else { 40 | echo_style('green', '.'); 41 | } 42 | } 43 | 44 | if ($checkPassed) { 45 | echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects', true); 46 | } else { 47 | echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects', true); 48 | 49 | echo_title('Fix the following mandatory requirements', 'red'); 50 | 51 | foreach ($messages['error'] as $helpText) { 52 | echo ' * '.$helpText.PHP_EOL; 53 | } 54 | } 55 | 56 | if (!empty($messages['warning'])) { 57 | echo_title('Optional recommendations to improve your setup', 'yellow'); 58 | 59 | foreach ($messages['warning'] as $helpText) { 60 | echo ' * '.$helpText.PHP_EOL; 61 | } 62 | } 63 | 64 | echo PHP_EOL; 65 | echo_style('title', 'Note'); 66 | echo ' The command console could use a different php.ini file'.PHP_EOL; 67 | echo_style('title', '~~~~'); 68 | echo ' than the one used with your web server. To be on the'.PHP_EOL; 69 | echo ' safe side, please check the requirements from your web'.PHP_EOL; 70 | echo ' server using the '; 71 | echo_style('yellow', 'web/config.php'); 72 | echo ' script.'.PHP_EOL; 73 | echo PHP_EOL; 74 | 75 | exit($checkPassed ? 0 : 1); 76 | 77 | function get_error_message(Requirement $requirement, $lineSize) 78 | { 79 | if ($requirement->isFulfilled()) { 80 | return; 81 | } 82 | 83 | $errorMessage = wordwrap($requirement->getTestMessage(), $lineSize - 3, PHP_EOL.' ').PHP_EOL; 84 | $errorMessage .= ' > '.wordwrap($requirement->getHelpText(), $lineSize - 5, PHP_EOL.' > ').PHP_EOL; 85 | 86 | return $errorMessage; 87 | } 88 | 89 | function echo_title($title, $style = null) 90 | { 91 | $style = $style ?: 'title'; 92 | 93 | echo PHP_EOL; 94 | echo_style($style, $title.PHP_EOL); 95 | echo_style($style, str_repeat('~', strlen($title)).PHP_EOL); 96 | echo PHP_EOL; 97 | } 98 | 99 | function echo_style($style, $message) 100 | { 101 | // ANSI color codes 102 | $styles = array( 103 | 'reset' => "\033[0m", 104 | 'red' => "\033[31m", 105 | 'green' => "\033[32m", 106 | 'yellow' => "\033[33m", 107 | 'error' => "\033[37;41m", 108 | 'success' => "\033[37;42m", 109 | 'title' => "\033[34m", 110 | ); 111 | $supports = has_color_support(); 112 | 113 | echo ($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : ''); 114 | } 115 | 116 | function echo_block($style, $title, $message) 117 | { 118 | $message = ' '.trim($message).' '; 119 | $width = strlen($message); 120 | 121 | echo PHP_EOL.PHP_EOL; 122 | 123 | echo_style($style, str_repeat(' ', $width).PHP_EOL); 124 | echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT).PHP_EOL); 125 | echo_style($style, str_pad($message, $width, ' ', STR_PAD_RIGHT).PHP_EOL); 126 | echo_style($style, str_repeat(' ', $width).PHP_EOL); 127 | } 128 | 129 | function has_color_support() 130 | { 131 | static $support; 132 | 133 | if (null === $support) { 134 | if (DIRECTORY_SEPARATOR == '\\') { 135 | $support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); 136 | } else { 137 | $support = function_exists('posix_isatty') && @posix_isatty(STDOUT); 138 | } 139 | } 140 | 141 | return $support; 142 | } 143 | -------------------------------------------------------------------------------- /app/config/config.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: parameters.yml } 3 | - { resource: security.yml } 4 | 5 | framework: 6 | #esi: ~ 7 | #translator: { fallback: "%locale%" } 8 | secret: "%secret%" 9 | router: 10 | resource: "%kernel.root_dir%/config/routing.yml" 11 | strict_requirements: ~ 12 | form: ~ 13 | csrf_protection: ~ 14 | validation: { enable_annotations: true } 15 | templating: 16 | engines: ['twig'] 17 | #assets_version: SomeVersionScheme 18 | default_locale: "%locale%" 19 | trusted_hosts: ~ 20 | trusted_proxies: ~ 21 | session: 22 | # handler_id set to null will use default session handler from php.ini 23 | handler_id: ~ 24 | fragments: ~ 25 | http_method_override: true 26 | 27 | # Twig Configuration 28 | twig: 29 | debug: "%kernel.debug%" 30 | strict_variables: "%kernel.debug%" 31 | 32 | # Assetic Configuration 33 | assetic: 34 | debug: "%kernel.debug%" 35 | use_controller: false 36 | bundles: [ ] 37 | #java: /usr/bin/java 38 | filters: 39 | cssrewrite: ~ 40 | #closure: 41 | # jar: "%kernel.root_dir%/Resources/java/compiler.jar" 42 | #yui_css: 43 | # jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar" 44 | 45 | # Doctrine Configuration 46 | doctrine: 47 | dbal: 48 | driver: "%database_driver%" 49 | host: "%database_host%" 50 | port: "%database_port%" 51 | dbname: "%database_name%" 52 | user: "%database_user%" 53 | password: "%database_password%" 54 | charset: UTF8 55 | # if using pdo_sqlite as your database driver, add the path in parameters.yml 56 | # e.g. database_path: "%kernel.root_dir%/data/data.db3" 57 | # path: "%database_path%" 58 | 59 | orm: 60 | auto_generate_proxy_classes: "%kernel.debug%" 61 | auto_mapping: true 62 | 63 | # Swiftmailer Configuration 64 | swiftmailer: 65 | transport: "%mailer_transport%" 66 | host: "%mailer_host%" 67 | username: "%mailer_user%" 68 | password: "%mailer_password%" 69 | spool: { type: memory } 70 | 71 | 72 | sensio_framework_extra: 73 | view: { annotations: false } 74 | 75 | 76 | fos_rest: 77 | view: 78 | view_response_listener: 'force' 79 | format_listener: 80 | rules: 81 | - { path: ^/api, priorities: [ json, xml ], fallback_format: json, prefer_extension: true } 82 | routing_loader: 83 | include_format: false 84 | param_fetcher_listener: true 85 | 86 | nelmio_api_doc: ~ -------------------------------------------------------------------------------- /app/config/config_codecept_test.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config_prod.yml } 3 | 4 | framework: 5 | profiler: { only_exceptions: false } 6 | 7 | doctrine: 8 | dbal: 9 | dbname: "%testing_database_name%" -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/config_test.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config_dev.yml } 3 | 4 | framework: 5 | test: ~ 6 | session: 7 | storage_id: session.storage.mock_file 8 | profiler: 9 | collect: false 10 | 11 | web_profiler: 12 | toolbar: false 13 | intercept_redirects: false 14 | 15 | swiftmailer: 16 | disable_delivery: true 17 | -------------------------------------------------------------------------------- /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/routing.yml: -------------------------------------------------------------------------------- 1 | NelmioApiDocBundle: 2 | resource: "@NelmioApiDocBundle/Resources/config/routing.yml" 3 | prefix: /api/doc 4 | 5 | 6 | 7 | rest_test_api: 8 | type: rest 9 | resource: "@CodeReviewRestBundle/Resources/config/routing.yml" 10 | prefix: /api -------------------------------------------------------------------------------- /app/config/routing_dev.yml: -------------------------------------------------------------------------------- 1 | _wdt: 2 | resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" 3 | prefix: /_wdt 4 | 5 | _profiler: 6 | resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" 7 | prefix: /_profiler 8 | 9 | _configurator: 10 | resource: "@SensioDistributionBundle/Resources/config/routing/webconfigurator.xml" 11 | prefix: /_configurator 12 | 13 | _main: 14 | resource: routing.yml 15 | -------------------------------------------------------------------------------- /app/config/security.yml: -------------------------------------------------------------------------------- 1 | security: 2 | 3 | encoders: 4 | Symfony\Component\Security\Core\User\User: plaintext 5 | 6 | providers: 7 | in_memory: 8 | memory: 9 | users: 10 | rest_user: { password: password123, roles: ['ROLE_API_USER'] } 11 | 12 | 13 | firewalls: 14 | dev: 15 | pattern: ^/(_(profiler|wdt)|css|images|js)/ 16 | security: false 17 | 18 | our_get_artists_firewall: 19 | pattern: ^/ 20 | anonymous: true 21 | http_basic: 22 | provider: in_memory 23 | methods: [GET] 24 | 25 | our_everything_else_artists_firewall: 26 | pattern: ^/ 27 | anonymous: false 28 | http_basic: 29 | provider: in_memory 30 | 31 | access_control: 32 | - { path: ^/api/artists/, roles: ROLE_API_USER } 33 | - { path: ^/api/artists, roles: IS_AUTHENTICATED_ANONYMOUSLY } -------------------------------------------------------------------------------- /app/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 | -------------------------------------------------------------------------------- /app/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | ../src/*/*Bundle/Tests 13 | ../src/*/Bundle/*Bundle/Tests 14 | 15 | 16 | 17 | 22 | 23 | 24 | 25 | ../src 26 | 27 | ../src/*/*Bundle/Resources 28 | ../src/*/*Bundle/Tests 29 | ../src/*/Bundle/*Bundle/Resources 30 | ../src/*/Bundle/*Bundle/Tests 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /bin/codecept: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | add(new Codeception\Command\Build('build')); 13 | $app->add(new Codeception\Command\Run('run')); 14 | $app->add(new Codeception\Command\Console('console')); 15 | $app->add(new Codeception\Command\Bootstrap('bootstrap')); 16 | $app->add(new Codeception\Command\GenerateCept('generate:cept')); 17 | $app->add(new Codeception\Command\GenerateCest('generate:cest')); 18 | $app->add(new Codeception\Command\GenerateTest('generate:test')); 19 | $app->add(new Codeception\Command\GeneratePhpUnit('generate:phpunit')); 20 | $app->add(new Codeception\Command\GenerateSuite('generate:suite')); 21 | $app->add(new Codeception\Command\GenerateHelper('generate:helper')); 22 | $app->add(new Codeception\Command\GenerateScenarios('generate:scenarios')); 23 | $app->add(new Codeception\Command\Clean('clean')); 24 | $app->add(new Codeception\Command\GenerateGroup('generate:group')); 25 | $app->add(new Codeception\Command\GeneratePageObject('generate:pageobject')); 26 | $app->add(new Codeception\Command\GenerateStepObject('generate:stepobject')); 27 | $app->run(); 28 | -------------------------------------------------------------------------------- /bin/doctrine: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | . 18 | */ 19 | 20 | use Symfony\Component\Console\Helper\HelperSet; 21 | use Doctrine\ORM\Tools\Console\ConsoleRunner; 22 | 23 | (@include_once __DIR__ . '/../vendor/autoload.php') || @include_once __DIR__ . '/../../../autoload.php'; 24 | 25 | $directories = array(getcwd(), getcwd() . DIRECTORY_SEPARATOR . 'config'); 26 | 27 | $configFile = null; 28 | foreach ($directories as $directory) { 29 | $configFile = $directory . DIRECTORY_SEPARATOR . 'cli-config.php'; 30 | 31 | if (file_exists($configFile)) { 32 | break; 33 | } 34 | } 35 | 36 | if ( ! file_exists($configFile)) { 37 | ConsoleRunner::printCliConfigTemplate(); 38 | exit(1); 39 | } 40 | 41 | if ( ! is_readable($configFile)) { 42 | echo 'Configuration file [' . $configFile . '] does not have read permission.' . "\n"; 43 | exit(1); 44 | } 45 | 46 | $commands = array(); 47 | 48 | $helperSet = require $configFile; 49 | 50 | if ( ! ($helperSet instanceof HelperSet)) { 51 | foreach ($GLOBALS as $helperSetCandidate) { 52 | if ($helperSetCandidate instanceof HelperSet) { 53 | $helperSet = $helperSetCandidate; 54 | break; 55 | } 56 | } 57 | } 58 | 59 | \Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet, $commands); 60 | -------------------------------------------------------------------------------- /bin/phpunit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | . 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 12 | * * Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 15 | * * Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in 17 | * the documentation and/or other materials provided with the 18 | * distribution. 19 | * 20 | * * Neither the name of Sebastian Bergmann nor the names of his 21 | * contributors may be used to endorse or promote products derived 22 | * from this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 | * POSSIBILITY OF SUCH DAMAGE. 36 | */ 37 | 38 | foreach (array(__DIR__ . '/../../autoload.php', __DIR__ . '/../vendor/autoload.php', __DIR__ . '/vendor/autoload.php') as $file) { 39 | if (file_exists($file)) { 40 | define('PHPUNIT_COMPOSER_INSTALL', $file); 41 | break; 42 | } 43 | } 44 | 45 | unset($file); 46 | 47 | if (!defined('PHPUNIT_COMPOSER_INSTALL')) { 48 | echo 'You need to set up the project dependencies using the following commands:' . PHP_EOL . 49 | 'wget http://getcomposer.org/composer.phar' . PHP_EOL . 50 | 'php composer.phar install' . PHP_EOL; 51 | die(1); 52 | } 53 | 54 | require PHPUNIT_COMPOSER_INSTALL; 55 | 56 | PHPUnit_TextUI_Command::main(); 57 | -------------------------------------------------------------------------------- /bin/security-checker: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | add(new SecurityCheckerCommand(new SecurityChecker())); 32 | $console->run(); 33 | -------------------------------------------------------------------------------- /codeception.yml: -------------------------------------------------------------------------------- 1 | actor: Guy 2 | paths: 3 | tests: tests 4 | log: tests/_log 5 | data: tests/_data 6 | helpers: tests/_helpers 7 | settings: 8 | bootstrap: _bootstrap.php 9 | colors: true 10 | memory_limit: 1024M 11 | modules: 12 | config: 13 | Db: 14 | dsn: 'mysql:host=127.0.0.1;dbname=rest_test' 15 | user: 'root' 16 | password: null 17 | dump: 'tests/_data/dump.sql' 18 | populate: true 19 | cleanup: true 20 | -------------------------------------------------------------------------------- /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": { "": "src/", "SymfonyStandard": "app/" } 8 | }, 9 | "require": { 10 | "php": ">=5.3.3", 11 | "symfony/symfony": "2.5.*", 12 | "doctrine/orm": "~2.2,>=2.2.3", 13 | "doctrine/doctrine-bundle": "~1.2", 14 | "twig/extensions": "~1.0", 15 | "symfony/assetic-bundle": "~2.3", 16 | "symfony/swiftmailer-bundle": "~2.3", 17 | "symfony/monolog-bundle": "~2.4", 18 | "sensio/distribution-bundle": "~3.0", 19 | "sensio/framework-extra-bundle": "~3.0", 20 | "incenteev/composer-parameter-handler": "~2.0", 21 | 22 | "doctrine/doctrine-fixtures-bundle": "2.2.*@dev", 23 | "friendsofsymfony/rest-bundle": "1.5.*@dev", 24 | "jms/serializer-bundle": "0.13.*@dev", 25 | "nelmio/api-doc-bundle": "2.7.*@dev" 26 | }, 27 | "require-dev": { 28 | "sensio/generator-bundle": "~2.3", 29 | "codeception/codeception": "2.1.*@dev" 30 | }, 31 | "scripts": { 32 | "post-root-package-install": [ 33 | "SymfonyStandard\\Composer::hookRootPackageInstall" 34 | ], 35 | "post-install-cmd": [ 36 | "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", 37 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", 38 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", 39 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", 40 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", 41 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles" 42 | ], 43 | "post-update-cmd": [ 44 | "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", 45 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", 46 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", 47 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", 48 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", 49 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles" 50 | ] 51 | }, 52 | "config": { 53 | "bin-dir": "bin" 54 | }, 55 | "extra": { 56 | "symfony-app-dir": "app", 57 | "symfony-web-dir": "web", 58 | "incenteev-parameters": { 59 | "file": "app/config/parameters.yml" 60 | }, 61 | "branch-alias": { 62 | "dev-master": "2.5-dev" 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/CodeReview/RestBundle/CodeReviewRestBundle.php: -------------------------------------------------------------------------------- 1 | getOr404($id); 42 | } 43 | 44 | /** 45 | * Returns a collection of Artists filtered by optional criteria 46 | * 47 | * @ApiDoc( 48 | * resource=true, 49 | * description="Returns a collection of Artists", 50 | * section="Artists", 51 | * requirements={ 52 | * {"name"="limit", "dataType"="integer", "requirement"="\d+", "description"="the max number of records to return"} 53 | * }, 54 | * parameters={ 55 | * {"name"="limit", "dataType"="integer", "required"=true, "description"="the max number of records to return"}, 56 | * {"name"="offset", "dataType"="integer", "required"=false, "description"="the record number to start results at"} 57 | * } 58 | * ) 59 | * 60 | * @QueryParam(name="limit", requirements="\d+", default="10", description="our limit") 61 | * @QueryParam(name="offset", requirements="\d+", nullable=true, default="0", description="our offset") 62 | * 63 | * @return array 64 | */ 65 | public function getArtistsAction(Request $request, ParamFetcherInterface $paramFetcher) 66 | { 67 | $limit = $paramFetcher->get('limit'); 68 | $offset = $paramFetcher->get('offset'); 69 | 70 | return $this->getHandler()->all($limit, $offset); 71 | } 72 | 73 | /** 74 | * @ApiDoc( 75 | * resource=true, 76 | * description="Creates a new Artist", 77 | * input = "CodeReview\RestBundle\Form\Type\ArtistFormType", 78 | * output = "CodeReview\RestBundle\Entity\Artist", 79 | * section="Artists", 80 | * statusCodes={ 81 | * 201="Returned when a new Artist has been successfully created", 82 | * 400="Returned when the posted data is invalid" 83 | * } 84 | * ) 85 | * 86 | * @View() 87 | * 88 | * @param Request $request 89 | * @return array|\FOS\RestBundle\View\View|null 90 | */ 91 | public function postArtistAction(Request $request) 92 | { 93 | try { 94 | 95 | $artist = $this->getHandler()->post( 96 | $request->request->all() 97 | ); 98 | 99 | $routeOptions = array( 100 | 'id' => $artist->getId(), 101 | '_format' => $request->get('_format'), 102 | ); 103 | 104 | return $this->routeRedirectView( 105 | 'get_artist', 106 | $routeOptions, 107 | Response::HTTP_CREATED 108 | ); 109 | 110 | } catch (InvalidFormException $e) { 111 | return $e->getForm(); 112 | } 113 | } 114 | 115 | 116 | /** 117 | * @ApiDoc( 118 | * resource=true, 119 | * description="Replaces an existing Artist", 120 | * input = "CodeReview\RestBundle\Form\Type\ArtistFormType", 121 | * output = "CodeReview\RestBundle\Entity\Artist", 122 | * section="Artists", 123 | * statusCodes={ 124 | * 201="Returned when a new Artist has been successfully created", 125 | * 204="Returned when an existing Artist has been successfully updated", 126 | * 400="Returned when the posted data is invalid" 127 | * } 128 | * ) 129 | * 130 | * @param Request $request 131 | * @param $id 132 | * @return array|\FOS\RestBundle\View\View|null 133 | */ 134 | public function putArtistAction(Request $request, $id) 135 | { 136 | $artist = $this->getHandler()->get($id); 137 | 138 | try { 139 | 140 | if ($artist === null) { 141 | $statusCode = Response::HTTP_CREATED; 142 | $artist = $this->getHandler()->post( 143 | $request->request->all() 144 | ); 145 | } else { 146 | $statusCode = Response::HTTP_NO_CONTENT; 147 | $artist = $this->getHandler()->put( 148 | $artist, 149 | $request->request->all() 150 | ); 151 | } 152 | 153 | $routeOptions = array( 154 | 'id' => $artist->getId(), 155 | '_format' => $request->get('_format') 156 | ); 157 | 158 | return $this->routeRedirectView('get_artist', $routeOptions, $statusCode); 159 | 160 | } catch (InvalidFormException $e) { 161 | return $e->getForm(); 162 | } 163 | } 164 | 165 | 166 | /** 167 | * @ApiDoc( 168 | * resource=true, 169 | * description="Updates an existing Artist", 170 | * input = "CodeReview\RestBundle\Form\Type\ArtistFormType", 171 | * output = "CodeReview\RestBundle\Entity\Artist", 172 | * section="Artists", 173 | * statusCodes={ 174 | * 204="Returned when an existing Artist has been successfully updated", 175 | * 400="Returned when the posted data is invalid", 176 | * 404="Returned when trying to update a non existent Artist" 177 | * } 178 | * ) 179 | * 180 | * @param Request $request 181 | * @param $id 182 | * @return array|\FOS\RestBundle\View\View|null 183 | */ 184 | public function patchArtistAction(Request $request, $id) 185 | { 186 | try { 187 | 188 | $artist = $this->getHandler()->patch( 189 | $this->getOr404($id), 190 | $request->request->all() 191 | ); 192 | 193 | $routeOptions = array( 194 | 'id' => $artist->getId(), 195 | '_format' => $request->get('_format') 196 | ); 197 | 198 | return $this->routeRedirectView('get_artist', $routeOptions, Response::HTTP_NO_CONTENT); 199 | 200 | } catch (InvalidFormException $e) { 201 | return $e->getForm(); 202 | } 203 | } 204 | 205 | 206 | /** 207 | * @ApiDoc( 208 | * resource=true, 209 | * description="Deletes an existing Artist", 210 | * section="Artists", 211 | * requirements={ 212 | * {"name"="id", "dataType"="integer", "requirement"="\d+", "description"="the id of the Artist to delete"} 213 | * }, 214 | * statusCodes={ 215 | * 204="Returned when an existing Artist has been successfully deleted", 216 | * 404="Returned when trying to delete a non existent Artist" 217 | * } 218 | * ) 219 | * 220 | * @param Request $request 221 | * @param $id 222 | */ 223 | public function deleteArtistAction(Request $request, $id) 224 | { 225 | $artist = $this->getOr404($id); 226 | 227 | $this->getHandler()->delete($artist); 228 | } 229 | 230 | /** 231 | * Returns a record by id, or throws a 404 error 232 | * 233 | * @param $id 234 | * @return mixed 235 | */ 236 | protected function getOr404($id) 237 | { 238 | $handler = $this->getHandler(); 239 | $data = $handler->get($id); 240 | 241 | if (null === $data) { 242 | throw new NotFoundHttpException(); 243 | } 244 | 245 | return $data; 246 | } 247 | 248 | /** 249 | * Returns the required handler for this controller 250 | * 251 | * @return \CodeReview\RestBundle\Handler\ArtistHandler 252 | */ 253 | private function getHandler() 254 | { 255 | return $this->get('code_review.rest_bundle.artist_handler'); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/CodeReview/RestBundle/DataFixtures/ORM/LoadArtistData.php: -------------------------------------------------------------------------------- 1 | getData() as $data) { 18 | 19 | $a = new Artist(); 20 | $a->setName($data['name']); 21 | $a->setDob($data['dob']); 22 | 23 | $manager->persist($a); 24 | $this->addReference($data['reference'], $a); 25 | 26 | } 27 | 28 | $manager->flush(); 29 | } 30 | 31 | /** 32 | * {@inheritDoc} 33 | */ 34 | public function getOrder() 35 | { 36 | return 100; // the order in which fixtures will be loaded 37 | } 38 | 39 | 40 | private function getData() 41 | { 42 | return array( 43 | array( 44 | 'name' => 'The Beatles', 45 | 'dob' => new \DateTime('-30 years'), 46 | 'reference' => 'artist-1', 47 | ), 48 | array( 49 | 'name' => 'The Rolling Stones', 50 | 'dob' => new \DateTime('-45 years 26 days 4 hours 6 minutes'), 51 | 'reference' => 'artist-2', 52 | ) 53 | ); 54 | } 55 | } -------------------------------------------------------------------------------- /src/CodeReview/RestBundle/DependencyInjection/CodeReviewRestExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration($configuration, $configs); 24 | 25 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 26 | $loader->load('services.yml'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/CodeReview/RestBundle/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | root('code_review_rest'); 22 | 23 | // Here you should define the parameters that are allowed to 24 | // configure your bundle. See the documentation linked above for 25 | // more information on that topic. 26 | 27 | return $treeBuilder; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/CodeReview/RestBundle/Entity/Artist.php: -------------------------------------------------------------------------------- 1 | id; 46 | } 47 | 48 | /** 49 | * Get name 50 | * 51 | * @return string 52 | */ 53 | public function getName() 54 | { 55 | return $this->name; 56 | } 57 | 58 | /** 59 | * @param mixed $name 60 | * @return Artist 61 | */ 62 | public function setName($name) 63 | { 64 | $this->name = $name; 65 | 66 | return $this; 67 | } 68 | 69 | /** 70 | * @return string 71 | */ 72 | public function getDob() 73 | { 74 | return $this->dob; 75 | } 76 | 77 | /** 78 | * @param string $dob 79 | * @return Artist 80 | */ 81 | public function setDob($dob) 82 | { 83 | $this->dob = $dob; 84 | 85 | return $this; 86 | } 87 | } -------------------------------------------------------------------------------- /src/CodeReview/RestBundle/Exception/InvalidFormException.php: -------------------------------------------------------------------------------- 1 | form = $form; 20 | } 21 | 22 | /** 23 | * @return array|null 24 | */ 25 | public function getForm() 26 | { 27 | return $this->form; 28 | } 29 | } -------------------------------------------------------------------------------- /src/CodeReview/RestBundle/Form/Handler/FormHandler.php: -------------------------------------------------------------------------------- 1 | em = $objectManager; 19 | $this->formFactory = $formFactory; 20 | $this->formType = $formType; 21 | } 22 | 23 | public function processForm($object, array $parameters, $method) 24 | { 25 | $form = $this->formFactory->create($this->formType, $object, array( 26 | 'method' => $method, 27 | 'csrf_protection' => false, 28 | )); 29 | 30 | $form->submit($parameters, 'PATCH' !== $method); 31 | 32 | if ( ! $form->isValid()) { 33 | throw new InvalidFormException($form); 34 | } 35 | 36 | $data = $form->getData(); 37 | $this->em->persist($data); 38 | $this->em->flush(); 39 | 40 | return $data; 41 | } 42 | 43 | public function delete($object) 44 | { 45 | $this->em->remove($object); 46 | $this->em->flush(); 47 | 48 | return true; 49 | } 50 | } -------------------------------------------------------------------------------- /src/CodeReview/RestBundle/Form/Type/ArtistType.php: -------------------------------------------------------------------------------- 1 | add('name', 'text', array( 19 | 'required' => true, 20 | )) 21 | ->add('dob', 'datetime', array( 22 | 'widget' => 'single_text', 23 | 'required' => true, 24 | )) 25 | ; 26 | } 27 | 28 | /** 29 | * @param OptionsResolverInterface $resolver 30 | */ 31 | public function setDefaultOptions(OptionsResolverInterface $resolver) 32 | { 33 | $resolver->setDefaults(array( 34 | 'data_class' => 'CodeReview\RestBundle\Entity\Artist' 35 | )); 36 | } 37 | 38 | /** 39 | * @return string 40 | */ 41 | public function getName() 42 | { 43 | return 'codereview_restbundle_artist'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/CodeReview/RestBundle/Handler/ArtistHandler.php: -------------------------------------------------------------------------------- 1 | repository = $artistRepository; 18 | $this->formHander = $formHandler; 19 | } 20 | 21 | /** 22 | * @param $id 23 | * @return mixed 24 | */ 25 | public function get($id) 26 | { 27 | return $this->repository->find($id); 28 | } 29 | 30 | /** 31 | * @param $limit 32 | * @param $offset 33 | * @return array 34 | */ 35 | public function all($limit, $offset) 36 | { 37 | return $this->repository->findBy(array(), array(), $limit, $offset); 38 | } 39 | 40 | /** 41 | * @param array $parameters 42 | * @return mixed 43 | */ 44 | public function post(array $parameters) 45 | { 46 | return $this->formHander->processForm( 47 | new Artist(), 48 | $parameters, 49 | "POST" 50 | ); 51 | } 52 | 53 | /** 54 | * @param ArtistInterface $artistInterface 55 | * @param array $parameters 56 | * @return mixed 57 | */ 58 | public function put(ArtistInterface $artistInterface, array $parameters) 59 | { 60 | return $this->formHander->processForm( 61 | $artistInterface, 62 | $parameters, 63 | "PUT" 64 | ); 65 | } 66 | 67 | /** 68 | * @param ArtistInterface $artistInterface 69 | * @param array $parameters 70 | * @return mixed 71 | */ 72 | public function patch(ArtistInterface $artistInterface, array $parameters) 73 | { 74 | return $this->formHander->processForm( 75 | $artistInterface, 76 | $parameters, 77 | "PATCH" 78 | ); 79 | } 80 | 81 | 82 | /** 83 | * @param ArtistInterface $artistInterface 84 | * @return mixed 85 | */ 86 | public function delete(ArtistInterface $artistInterface) 87 | { 88 | return $this->formHander->delete($artistInterface); 89 | } 90 | 91 | 92 | } -------------------------------------------------------------------------------- /src/CodeReview/RestBundle/Handler/HandlerInterface.php: -------------------------------------------------------------------------------- 1 | request('GET', '/hello/Fabien'); 14 | 15 | $this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/_bootstrap.php: -------------------------------------------------------------------------------- 1 | scenario->runStep(new \Codeception\Step\Action('setHeader', func_get_args())); 34 | } 35 | 36 | 37 | /** 38 | * [!] Method is generated. Documentation taken from corresponding module. 39 | * 40 | * Sets 'url' configuration parameter to hosts subdomain. 41 | * It does not open a page on subdomain. Use `amOnPage` for that 42 | * 43 | * ``` php 44 | * amOnSubdomain('user'); 50 | * $I->amOnPage('/'); 51 | * // moves to http://user.mysite.com/ 52 | * ?> 53 | * ``` 54 | * 55 | * @param $subdomain 56 | * 57 | * @return mixed 58 | * @see \Codeception\Module\PhpBrowser::amOnSubdomain() 59 | */ 60 | public function amOnSubdomain($subdomain) { 61 | return $this->scenario->runStep(new \Codeception\Step\Condition('amOnSubdomain', func_get_args())); 62 | } 63 | 64 | 65 | /** 66 | * [!] Method is generated. Documentation taken from corresponding module. 67 | * 68 | * Low-level API method. 69 | * If Codeception commands are not enough, use [Guzzle HTTP Client](http://guzzlephp.org/) methods directly 70 | * 71 | * Example: 72 | * 73 | * ``` php 74 | * amGoingTo('Sign all requests with OAuth'); 77 | * $I->executeInGuzzle(function (\Guzzle\Http\Client $client) { 78 | * $client->addSubscriber(new Guzzle\Plugin\Oauth\OauthPlugin(array( 79 | * 'consumer_key' => '***', 80 | * 'consumer_secret' => '***', 81 | * 'token' => '***', 82 | * 'token_secret' => '***' 83 | * ))); 84 | * }); 85 | * ?> 86 | * ``` 87 | * 88 | * It is not recommended to use this command on a regular basis. 89 | * If Codeception lacks important Guzzle Client methods, implement them and submit patches. 90 | * 91 | * @param callable $function 92 | * @see \Codeception\Module\PhpBrowser::executeInGuzzle() 93 | */ 94 | public function executeInGuzzle($function) { 95 | return $this->scenario->runStep(new \Codeception\Step\Action('executeInGuzzle', func_get_args())); 96 | } 97 | 98 | 99 | /** 100 | * [!] Method is generated. Documentation taken from corresponding module. 101 | * 102 | * Authenticates user for HTTP_AUTH 103 | * 104 | * @param $username 105 | * @param $password 106 | * @see \Codeception\Lib\InnerBrowser::amHttpAuthenticated() 107 | */ 108 | public function amHttpAuthenticated($username, $password) { 109 | return $this->scenario->runStep(new \Codeception\Step\Condition('amHttpAuthenticated', func_get_args())); 110 | } 111 | 112 | 113 | /** 114 | * [!] Method is generated. Documentation taken from corresponding module. 115 | * 116 | * Opens the page. 117 | * Requires relative uri as parameter 118 | * 119 | * Example: 120 | * 121 | * ``` php 122 | * amOnPage('/'); 125 | * // opens /register page 126 | * $I->amOnPage('/register'); 127 | * ?> 128 | * ``` 129 | * 130 | * @param $page 131 | * @see \Codeception\Lib\InnerBrowser::amOnPage() 132 | */ 133 | public function amOnPage($page) { 134 | return $this->scenario->runStep(new \Codeception\Step\Condition('amOnPage', func_get_args())); 135 | } 136 | 137 | 138 | /** 139 | * [!] Method is generated. Documentation taken from corresponding module. 140 | * 141 | * Perform a click on link or button. 142 | * Link or button are found by their names or CSS selector. 143 | * Submits a form if button is a submit type. 144 | * 145 | * If link is an image it's found by alt attribute value of image. 146 | * If button is image button is found by it's value 147 | * If link or button can't be found by name they are searched by CSS selector. 148 | * 149 | * The second parameter is a context: CSS or XPath locator to narrow the search. 150 | * 151 | * Examples: 152 | * 153 | * ``` php 154 | * click('Logout'); 157 | * // button of form 158 | * $I->click('Submit'); 159 | * // CSS button 160 | * $I->click('#form input[type=submit]'); 161 | * // XPath 162 | * $I->click('//form/*[@type=submit]') 163 | * // link in context 164 | * $I->click('Logout', '#nav'); 165 | * ?> 166 | * ``` 167 | * 168 | * @param $link 169 | * @param $context 170 | * @see \Codeception\Lib\InnerBrowser::click() 171 | */ 172 | public function click($link, $context = null) { 173 | return $this->scenario->runStep(new \Codeception\Step\Action('click', func_get_args())); 174 | } 175 | 176 | 177 | /** 178 | * [!] Method is generated. Documentation taken from corresponding module. 179 | * 180 | * Check if current page contains the text specified. 181 | * Specify the css selector to match only specific region. 182 | * 183 | * Examples: 184 | * 185 | * ``` php 186 | * see('Logout'); // I can suppose user is logged in 188 | * $I->see('Sign Up','h1'); // I can suppose it's a signup page 189 | * $I->see('Sign Up','//body/h1'); // with XPath 190 | * ?> 191 | * ``` 192 | * 193 | * @param $text 194 | * @param null $selector 195 | * Conditional Assertion: Test won't be stopped on fail 196 | * @see \Codeception\Lib\InnerBrowser::see() 197 | */ 198 | public function canSee($text, $selector = null) { 199 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('see', func_get_args())); 200 | } 201 | /** 202 | * [!] Method is generated. Documentation taken from corresponding module. 203 | * 204 | * Check if current page contains the text specified. 205 | * Specify the css selector to match only specific region. 206 | * 207 | * Examples: 208 | * 209 | * ``` php 210 | * see('Logout'); // I can suppose user is logged in 212 | * $I->see('Sign Up','h1'); // I can suppose it's a signup page 213 | * $I->see('Sign Up','//body/h1'); // with XPath 214 | * ?> 215 | * ``` 216 | * 217 | * @param $text 218 | * @param null $selector 219 | * @see \Codeception\Lib\InnerBrowser::see() 220 | */ 221 | public function see($text, $selector = null) { 222 | return $this->scenario->runStep(new \Codeception\Step\Assertion('see', func_get_args())); 223 | } 224 | 225 | 226 | /** 227 | * [!] Method is generated. Documentation taken from corresponding module. 228 | * 229 | * Check if current page doesn't contain the text specified. 230 | * Specify the css selector to match only specific region. 231 | * 232 | * Examples: 233 | * 234 | * ```php 235 | * dontSee('Login'); // I can suppose user is already logged in 237 | * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page 238 | * $I->dontSee('Sign Up','//body/h1'); // with XPath 239 | * ?> 240 | * ``` 241 | * 242 | * @param $text 243 | * @param null $selector 244 | * Conditional Assertion: Test won't be stopped on fail 245 | * @see \Codeception\Lib\InnerBrowser::dontSee() 246 | */ 247 | public function cantSee($text, $selector = null) { 248 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSee', func_get_args())); 249 | } 250 | /** 251 | * [!] Method is generated. Documentation taken from corresponding module. 252 | * 253 | * Check if current page doesn't contain the text specified. 254 | * Specify the css selector to match only specific region. 255 | * 256 | * Examples: 257 | * 258 | * ```php 259 | * dontSee('Login'); // I can suppose user is already logged in 261 | * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page 262 | * $I->dontSee('Sign Up','//body/h1'); // with XPath 263 | * ?> 264 | * ``` 265 | * 266 | * @param $text 267 | * @param null $selector 268 | * @see \Codeception\Lib\InnerBrowser::dontSee() 269 | */ 270 | public function dontSee($text, $selector = null) { 271 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSee', func_get_args())); 272 | } 273 | 274 | 275 | /** 276 | * [!] Method is generated. Documentation taken from corresponding module. 277 | * 278 | * Checks if there is a link with text specified. 279 | * Specify url to match link with exact this url. 280 | * 281 | * Examples: 282 | * 283 | * ``` php 284 | * seeLink('Logout'); // matches Logout 286 | * $I->seeLink('Logout','/logout'); // matches Logout 287 | * ?> 288 | * ``` 289 | * 290 | * @param $text 291 | * @param null $url 292 | * Conditional Assertion: Test won't be stopped on fail 293 | * @see \Codeception\Lib\InnerBrowser::seeLink() 294 | */ 295 | public function canSeeLink($text, $url = null) { 296 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeLink', func_get_args())); 297 | } 298 | /** 299 | * [!] Method is generated. Documentation taken from corresponding module. 300 | * 301 | * Checks if there is a link with text specified. 302 | * Specify url to match link with exact this url. 303 | * 304 | * Examples: 305 | * 306 | * ``` php 307 | * seeLink('Logout'); // matches Logout 309 | * $I->seeLink('Logout','/logout'); // matches Logout 310 | * ?> 311 | * ``` 312 | * 313 | * @param $text 314 | * @param null $url 315 | * @see \Codeception\Lib\InnerBrowser::seeLink() 316 | */ 317 | public function seeLink($text, $url = null) { 318 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeLink', func_get_args())); 319 | } 320 | 321 | 322 | /** 323 | * [!] Method is generated. Documentation taken from corresponding module. 324 | * 325 | * Checks if page doesn't contain the link with text specified. 326 | * Specify url to narrow the results. 327 | * 328 | * Examples: 329 | * 330 | * ``` php 331 | * dontSeeLink('Logout'); // I suppose user is not logged in 333 | * ?> 334 | * ``` 335 | * 336 | * @param $text 337 | * @param null $url 338 | * Conditional Assertion: Test won't be stopped on fail 339 | * @see \Codeception\Lib\InnerBrowser::dontSeeLink() 340 | */ 341 | public function cantSeeLink($text, $url = null) { 342 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeLink', func_get_args())); 343 | } 344 | /** 345 | * [!] Method is generated. Documentation taken from corresponding module. 346 | * 347 | * Checks if page doesn't contain the link with text specified. 348 | * Specify url to narrow the results. 349 | * 350 | * Examples: 351 | * 352 | * ``` php 353 | * dontSeeLink('Logout'); // I suppose user is not logged in 355 | * ?> 356 | * ``` 357 | * 358 | * @param $text 359 | * @param null $url 360 | * @see \Codeception\Lib\InnerBrowser::dontSeeLink() 361 | */ 362 | public function dontSeeLink($text, $url = null) { 363 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeLink', func_get_args())); 364 | } 365 | 366 | 367 | /** 368 | * [!] Method is generated. Documentation taken from corresponding module. 369 | * 370 | * Checks that current uri contains a value 371 | * 372 | * ``` php 373 | * seeInCurrentUrl('home'); 376 | * // to match: /users/1 377 | * $I->seeInCurrentUrl('/users/'); 378 | * ?> 379 | * ``` 380 | * 381 | * @param $uri 382 | * Conditional Assertion: Test won't be stopped on fail 383 | * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl() 384 | */ 385 | public function canSeeInCurrentUrl($uri) { 386 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInCurrentUrl', func_get_args())); 387 | } 388 | /** 389 | * [!] Method is generated. Documentation taken from corresponding module. 390 | * 391 | * Checks that current uri contains a value 392 | * 393 | * ``` php 394 | * seeInCurrentUrl('home'); 397 | * // to match: /users/1 398 | * $I->seeInCurrentUrl('/users/'); 399 | * ?> 400 | * ``` 401 | * 402 | * @param $uri 403 | * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl() 404 | */ 405 | public function seeInCurrentUrl($uri) { 406 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInCurrentUrl', func_get_args())); 407 | } 408 | 409 | 410 | /** 411 | * [!] Method is generated. Documentation taken from corresponding module. 412 | * 413 | * Checks that current uri does not contain a value 414 | * 415 | * ``` php 416 | * dontSeeInCurrentUrl('/users/'); 418 | * ?> 419 | * ``` 420 | * 421 | * @param $uri 422 | * Conditional Assertion: Test won't be stopped on fail 423 | * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl() 424 | */ 425 | public function cantSeeInCurrentUrl($uri) { 426 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInCurrentUrl', func_get_args())); 427 | } 428 | /** 429 | * [!] Method is generated. Documentation taken from corresponding module. 430 | * 431 | * Checks that current uri does not contain a value 432 | * 433 | * ``` php 434 | * dontSeeInCurrentUrl('/users/'); 436 | * ?> 437 | * ``` 438 | * 439 | * @param $uri 440 | * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl() 441 | */ 442 | public function dontSeeInCurrentUrl($uri) { 443 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInCurrentUrl', func_get_args())); 444 | } 445 | 446 | 447 | /** 448 | * [!] Method is generated. Documentation taken from corresponding module. 449 | * 450 | * Checks that current url is equal to value. 451 | * Unlike `seeInCurrentUrl` performs a strict check. 452 | * 453 | * ``` php 454 | * seeCurrentUrlEquals('/'); 457 | * ?> 458 | * ``` 459 | * 460 | * @param $uri 461 | * Conditional Assertion: Test won't be stopped on fail 462 | * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals() 463 | */ 464 | public function canSeeCurrentUrlEquals($uri) { 465 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlEquals', func_get_args())); 466 | } 467 | /** 468 | * [!] Method is generated. Documentation taken from corresponding module. 469 | * 470 | * Checks that current url is equal to value. 471 | * Unlike `seeInCurrentUrl` performs a strict check. 472 | * 473 | * ``` php 474 | * seeCurrentUrlEquals('/'); 477 | * ?> 478 | * ``` 479 | * 480 | * @param $uri 481 | * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals() 482 | */ 483 | public function seeCurrentUrlEquals($uri) { 484 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCurrentUrlEquals', func_get_args())); 485 | } 486 | 487 | 488 | /** 489 | * [!] Method is generated. Documentation taken from corresponding module. 490 | * 491 | * Checks that current url is not equal to value. 492 | * Unlike `dontSeeInCurrentUrl` performs a strict check. 493 | * 494 | * ``` php 495 | * dontSeeCurrentUrlEquals('/'); 498 | * ?> 499 | * ``` 500 | * 501 | * @param $uri 502 | * Conditional Assertion: Test won't be stopped on fail 503 | * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals() 504 | */ 505 | public function cantSeeCurrentUrlEquals($uri) { 506 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlEquals', func_get_args())); 507 | } 508 | /** 509 | * [!] Method is generated. Documentation taken from corresponding module. 510 | * 511 | * Checks that current url is not equal to value. 512 | * Unlike `dontSeeInCurrentUrl` performs a strict check. 513 | * 514 | * ``` php 515 | * dontSeeCurrentUrlEquals('/'); 518 | * ?> 519 | * ``` 520 | * 521 | * @param $uri 522 | * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals() 523 | */ 524 | public function dontSeeCurrentUrlEquals($uri) { 525 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlEquals', func_get_args())); 526 | } 527 | 528 | 529 | /** 530 | * [!] Method is generated. Documentation taken from corresponding module. 531 | * 532 | * Checks that current url is matches a RegEx value 533 | * 534 | * ``` php 535 | * seeCurrentUrlMatches('~$/users/(\d+)~'); 538 | * ?> 539 | * ``` 540 | * 541 | * @param $uri 542 | * Conditional Assertion: Test won't be stopped on fail 543 | * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches() 544 | */ 545 | public function canSeeCurrentUrlMatches($uri) { 546 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlMatches', func_get_args())); 547 | } 548 | /** 549 | * [!] Method is generated. Documentation taken from corresponding module. 550 | * 551 | * Checks that current url is matches a RegEx value 552 | * 553 | * ``` php 554 | * seeCurrentUrlMatches('~$/users/(\d+)~'); 557 | * ?> 558 | * ``` 559 | * 560 | * @param $uri 561 | * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches() 562 | */ 563 | public function seeCurrentUrlMatches($uri) { 564 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCurrentUrlMatches', func_get_args())); 565 | } 566 | 567 | 568 | /** 569 | * [!] Method is generated. Documentation taken from corresponding module. 570 | * 571 | * Checks that current url does not match a RegEx value 572 | * 573 | * ``` php 574 | * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); 577 | * ?> 578 | * ``` 579 | * 580 | * @param $uri 581 | * Conditional Assertion: Test won't be stopped on fail 582 | * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches() 583 | */ 584 | public function cantSeeCurrentUrlMatches($uri) { 585 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlMatches', func_get_args())); 586 | } 587 | /** 588 | * [!] Method is generated. Documentation taken from corresponding module. 589 | * 590 | * Checks that current url does not match a RegEx value 591 | * 592 | * ``` php 593 | * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); 596 | * ?> 597 | * ``` 598 | * 599 | * @param $uri 600 | * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches() 601 | */ 602 | public function dontSeeCurrentUrlMatches($uri) { 603 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlMatches', func_get_args())); 604 | } 605 | 606 | 607 | /** 608 | * [!] Method is generated. Documentation taken from corresponding module. 609 | * 610 | * Takes a parameters from current URI by RegEx. 611 | * If no url provided returns full URI. 612 | * 613 | * ``` php 614 | * grabFromCurrentUrl('~$/user/(\d+)/~'); 616 | * $uri = $I->grabFromCurrentUrl(); 617 | * ?> 618 | * ``` 619 | * 620 | * @param null $uri 621 | * 622 | * @internal param $url 623 | * @return mixed 624 | * @see \Codeception\Lib\InnerBrowser::grabFromCurrentUrl() 625 | */ 626 | public function grabFromCurrentUrl($uri = null) { 627 | return $this->scenario->runStep(new \Codeception\Step\Action('grabFromCurrentUrl', func_get_args())); 628 | } 629 | 630 | 631 | /** 632 | * [!] Method is generated. Documentation taken from corresponding module. 633 | * 634 | * Assert if the specified checkbox is checked. 635 | * Use css selector or xpath to match. 636 | * 637 | * Example: 638 | * 639 | * ``` php 640 | * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms 642 | * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. 643 | * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); 644 | * ?> 645 | * ``` 646 | * 647 | * @param $checkbox 648 | * Conditional Assertion: Test won't be stopped on fail 649 | * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked() 650 | */ 651 | public function canSeeCheckboxIsChecked($checkbox) { 652 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCheckboxIsChecked', func_get_args())); 653 | } 654 | /** 655 | * [!] Method is generated. Documentation taken from corresponding module. 656 | * 657 | * Assert if the specified checkbox is checked. 658 | * Use css selector or xpath to match. 659 | * 660 | * Example: 661 | * 662 | * ``` php 663 | * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms 665 | * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. 666 | * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); 667 | * ?> 668 | * ``` 669 | * 670 | * @param $checkbox 671 | * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked() 672 | */ 673 | public function seeCheckboxIsChecked($checkbox) { 674 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCheckboxIsChecked', func_get_args())); 675 | } 676 | 677 | 678 | /** 679 | * [!] Method is generated. Documentation taken from corresponding module. 680 | * 681 | * Assert if the specified checkbox is unchecked. 682 | * Use css selector or xpath to match. 683 | * 684 | * Example: 685 | * 686 | * ``` php 687 | * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms 689 | * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. 690 | * ?> 691 | * ``` 692 | * 693 | * @param $checkbox 694 | * Conditional Assertion: Test won't be stopped on fail 695 | * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked() 696 | */ 697 | public function cantSeeCheckboxIsChecked($checkbox) { 698 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCheckboxIsChecked', func_get_args())); 699 | } 700 | /** 701 | * [!] Method is generated. Documentation taken from corresponding module. 702 | * 703 | * Assert if the specified checkbox is unchecked. 704 | * Use css selector or xpath to match. 705 | * 706 | * Example: 707 | * 708 | * ``` php 709 | * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms 711 | * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. 712 | * ?> 713 | * ``` 714 | * 715 | * @param $checkbox 716 | * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked() 717 | */ 718 | public function dontSeeCheckboxIsChecked($checkbox) { 719 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCheckboxIsChecked', func_get_args())); 720 | } 721 | 722 | 723 | /** 724 | * [!] Method is generated. Documentation taken from corresponding module. 725 | * 726 | * Checks that an input field or textarea contains value. 727 | * Field is matched either by label or CSS or Xpath 728 | * 729 | * Example: 730 | * 731 | * ``` php 732 | * seeInField('Body','Type your comment here'); 734 | * $I->seeInField('form textarea[name=body]','Type your comment here'); 735 | * $I->seeInField('form input[type=hidden]','hidden_value'); 736 | * $I->seeInField('#searchform input','Search'); 737 | * $I->seeInField('//form/*[@name=search]','Search'); 738 | * ?> 739 | * ``` 740 | * 741 | * @param $field 742 | * @param $value 743 | * Conditional Assertion: Test won't be stopped on fail 744 | * @see \Codeception\Lib\InnerBrowser::seeInField() 745 | */ 746 | public function canSeeInField($field, $value) { 747 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInField', func_get_args())); 748 | } 749 | /** 750 | * [!] Method is generated. Documentation taken from corresponding module. 751 | * 752 | * Checks that an input field or textarea contains value. 753 | * Field is matched either by label or CSS or Xpath 754 | * 755 | * Example: 756 | * 757 | * ``` php 758 | * seeInField('Body','Type your comment here'); 760 | * $I->seeInField('form textarea[name=body]','Type your comment here'); 761 | * $I->seeInField('form input[type=hidden]','hidden_value'); 762 | * $I->seeInField('#searchform input','Search'); 763 | * $I->seeInField('//form/*[@name=search]','Search'); 764 | * ?> 765 | * ``` 766 | * 767 | * @param $field 768 | * @param $value 769 | * @see \Codeception\Lib\InnerBrowser::seeInField() 770 | */ 771 | public function seeInField($field, $value) { 772 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInField', func_get_args())); 773 | } 774 | 775 | 776 | /** 777 | * [!] Method is generated. Documentation taken from corresponding module. 778 | * 779 | * Checks that an input field or textarea doesn't contain value. 780 | * Field is matched either by label or CSS or Xpath 781 | * Example: 782 | * 783 | * ``` php 784 | * dontSeeInField('Body','Type your comment here'); 786 | * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); 787 | * $I->dontSeeInField('form input[type=hidden]','hidden_value'); 788 | * $I->dontSeeInField('#searchform input','Search'); 789 | * $I->dontSeeInField('//form/*[@name=search]','Search'); 790 | * ?> 791 | * ``` 792 | * 793 | * @param $field 794 | * @param $value 795 | * Conditional Assertion: Test won't be stopped on fail 796 | * @see \Codeception\Lib\InnerBrowser::dontSeeInField() 797 | */ 798 | public function cantSeeInField($field, $value) { 799 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInField', func_get_args())); 800 | } 801 | /** 802 | * [!] Method is generated. Documentation taken from corresponding module. 803 | * 804 | * Checks that an input field or textarea doesn't contain value. 805 | * Field is matched either by label or CSS or Xpath 806 | * Example: 807 | * 808 | * ``` php 809 | * dontSeeInField('Body','Type your comment here'); 811 | * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); 812 | * $I->dontSeeInField('form input[type=hidden]','hidden_value'); 813 | * $I->dontSeeInField('#searchform input','Search'); 814 | * $I->dontSeeInField('//form/*[@name=search]','Search'); 815 | * ?> 816 | * ``` 817 | * 818 | * @param $field 819 | * @param $value 820 | * @see \Codeception\Lib\InnerBrowser::dontSeeInField() 821 | */ 822 | public function dontSeeInField($field, $value) { 823 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInField', func_get_args())); 824 | } 825 | 826 | 827 | /** 828 | * [!] Method is generated. Documentation taken from corresponding module. 829 | * 830 | * Submits a form located on page. 831 | * Specify the form by it's css or xpath selector. 832 | * Fill the form fields values as array. 833 | * 834 | * Skipped fields will be filled by their values from page. 835 | * You don't need to click the 'Submit' button afterwards. 836 | * This command itself triggers the request to form's action. 837 | * 838 | * Examples: 839 | * 840 | * ``` php 841 | * submitForm('#login', array('login' => 'davert', 'password' => '123456')); 843 | * 844 | * ``` 845 | * 846 | * For sample Sign Up form: 847 | * 848 | * ``` html 849 | *
850 | * Login:
851 | * Password:
852 | * Do you agree to out terms?
853 | * Select pricing plan 854 | * 855 | *
856 | * ``` 857 | * I can write this: 858 | * 859 | * ``` php 860 | * submitForm('#userForm', array('user' => array('login' => 'Davert', 'password' => '123456', 'agree' => true))); 862 | * 863 | * ``` 864 | * Note, that pricing plan will be set to Paid, as it's selected on page. 865 | * 866 | * @param $selector 867 | * @param $params 868 | * @see \Codeception\Lib\InnerBrowser::submitForm() 869 | */ 870 | public function submitForm($selector, $params) { 871 | return $this->scenario->runStep(new \Codeception\Step\Action('submitForm', func_get_args())); 872 | } 873 | 874 | 875 | /** 876 | * [!] Method is generated. Documentation taken from corresponding module. 877 | * 878 | * Fills a text field or textarea with value. 879 | * 880 | * Example: 881 | * 882 | * ``` php 883 | * fillField("//input[@type='text']", "Hello World!"); 885 | * ?> 886 | * ``` 887 | * 888 | * @param $field 889 | * @param $value 890 | * @see \Codeception\Lib\InnerBrowser::fillField() 891 | */ 892 | public function fillField($field, $value) { 893 | return $this->scenario->runStep(new \Codeception\Step\Action('fillField', func_get_args())); 894 | } 895 | 896 | 897 | /** 898 | * [!] Method is generated. Documentation taken from corresponding module. 899 | * 900 | * Selects an option in select tag or in radio button group. 901 | * 902 | * Example: 903 | * 904 | * ``` php 905 | * selectOption('form select[name=account]', 'Premium'); 907 | * $I->selectOption('form input[name=payment]', 'Monthly'); 908 | * $I->selectOption('//form/select[@name=account]', 'Monthly'); 909 | * ?> 910 | * ``` 911 | * 912 | * Can select multiple options if second argument is array: 913 | * 914 | * ``` php 915 | * selectOption('Which OS do you use?', array('Windows','Linux')); 917 | * ?> 918 | * ``` 919 | * 920 | * @param $select 921 | * @param $option 922 | * @see \Codeception\Lib\InnerBrowser::selectOption() 923 | */ 924 | public function selectOption($select, $option) { 925 | return $this->scenario->runStep(new \Codeception\Step\Action('selectOption', func_get_args())); 926 | } 927 | 928 | 929 | /** 930 | * [!] Method is generated. Documentation taken from corresponding module. 931 | * 932 | * Ticks a checkbox. 933 | * For radio buttons use `selectOption` method. 934 | * 935 | * Example: 936 | * 937 | * ``` php 938 | * checkOption('#agree'); 940 | * ?> 941 | * ``` 942 | * 943 | * @param $option 944 | * @see \Codeception\Lib\InnerBrowser::checkOption() 945 | */ 946 | public function checkOption($option) { 947 | return $this->scenario->runStep(new \Codeception\Step\Action('checkOption', func_get_args())); 948 | } 949 | 950 | 951 | /** 952 | * [!] Method is generated. Documentation taken from corresponding module. 953 | * 954 | * Unticks a checkbox. 955 | * 956 | * Example: 957 | * 958 | * ``` php 959 | * uncheckOption('#notify'); 961 | * ?> 962 | * ``` 963 | * 964 | * @param $option 965 | * @see \Codeception\Lib\InnerBrowser::uncheckOption() 966 | */ 967 | public function uncheckOption($option) { 968 | return $this->scenario->runStep(new \Codeception\Step\Action('uncheckOption', func_get_args())); 969 | } 970 | 971 | 972 | /** 973 | * [!] Method is generated. Documentation taken from corresponding module. 974 | * 975 | * Attaches file from Codeception data directory to upload field. 976 | * 977 | * Example: 978 | * 979 | * ``` php 980 | * attachFile('input[@type="file"]', 'prices.xls'); 983 | * ?> 984 | * ``` 985 | * 986 | * @param $field 987 | * @param $filename 988 | * @see \Codeception\Lib\InnerBrowser::attachFile() 989 | */ 990 | public function attachFile($field, $filename) { 991 | return $this->scenario->runStep(new \Codeception\Step\Action('attachFile', func_get_args())); 992 | } 993 | 994 | 995 | /** 996 | * [!] Method is generated. Documentation taken from corresponding module. 997 | * 998 | * If your page triggers an ajax request, you can perform it manually. 999 | * This action sends a GET ajax request with specified params. 1000 | * 1001 | * See ->sendAjaxPostRequest for examples. 1002 | * 1003 | * @param $uri 1004 | * @param $params 1005 | * @see \Codeception\Lib\InnerBrowser::sendAjaxGetRequest() 1006 | */ 1007 | public function sendAjaxGetRequest($uri, $params = null) { 1008 | return $this->scenario->runStep(new \Codeception\Step\Action('sendAjaxGetRequest', func_get_args())); 1009 | } 1010 | 1011 | 1012 | /** 1013 | * [!] Method is generated. Documentation taken from corresponding module. 1014 | * 1015 | * If your page triggers an ajax request, you can perform it manually. 1016 | * This action sends a POST ajax request with specified params. 1017 | * Additional params can be passed as array. 1018 | * 1019 | * Example: 1020 | * 1021 | * Imagine that by clicking checkbox you trigger ajax request which updates user settings. 1022 | * We emulate that click by running this ajax request manually. 1023 | * 1024 | * ``` php 1025 | * sendAjaxPostRequest('/updateSettings', array('notifications' => true)); // POST 1027 | * $I->sendAjaxGetRequest('/updateSettings', array('notifications' => true)); // GET 1028 | * 1029 | * ``` 1030 | * 1031 | * @param $uri 1032 | * @param $params 1033 | * @see \Codeception\Lib\InnerBrowser::sendAjaxPostRequest() 1034 | */ 1035 | public function sendAjaxPostRequest($uri, $params = null) { 1036 | return $this->scenario->runStep(new \Codeception\Step\Action('sendAjaxPostRequest', func_get_args())); 1037 | } 1038 | 1039 | 1040 | /** 1041 | * [!] Method is generated. Documentation taken from corresponding module. 1042 | * 1043 | * If your page triggers an ajax request, you can perform it manually. 1044 | * This action sends an ajax request with specified method and params. 1045 | * 1046 | * Example: 1047 | * 1048 | * You need to perform an ajax request specifying the HTTP method. 1049 | * 1050 | * ``` php 1051 | * sendAjaxRequest('PUT', /posts/7', array('title' => 'new title'); 1053 | * 1054 | * ``` 1055 | * 1056 | * @param $method 1057 | * @param $uri 1058 | * @param $params 1059 | * @see \Codeception\Lib\InnerBrowser::sendAjaxRequest() 1060 | */ 1061 | public function sendAjaxRequest($method, $uri, $params = null) { 1062 | return $this->scenario->runStep(new \Codeception\Step\Action('sendAjaxRequest', func_get_args())); 1063 | } 1064 | 1065 | 1066 | /** 1067 | * [!] Method is generated. Documentation taken from corresponding module. 1068 | * 1069 | * Finds and returns text contents of element. 1070 | * Element is searched by CSS selector, XPath or matcher by regex. 1071 | * 1072 | * Example: 1073 | * 1074 | * ``` php 1075 | * grabTextFrom('h1'); 1077 | * $heading = $I->grabTextFrom('descendant-or-self::h1'); 1078 | * $value = $I->grabTextFrom('~ 1080 | * ``` 1081 | * 1082 | * @param $cssOrXPathOrRegex 1083 | * 1084 | * @return mixed 1085 | * @see \Codeception\Lib\InnerBrowser::grabTextFrom() 1086 | */ 1087 | public function grabTextFrom($cssOrXPathOrRegex) { 1088 | return $this->scenario->runStep(new \Codeception\Step\Action('grabTextFrom', func_get_args())); 1089 | } 1090 | 1091 | 1092 | /** 1093 | * [!] Method is generated. Documentation taken from corresponding module. 1094 | * 1095 | * Grabs attribute value from an element. 1096 | * Fails if element is not found. 1097 | * 1098 | * ``` php 1099 | * grabAttributeFrom('#tooltip', 'title'); 1101 | * ?> 1102 | * ``` 1103 | * 1104 | * 1105 | * @param $cssOrXpath 1106 | * @param $attribute 1107 | * @internal param $element 1108 | * @return mixed 1109 | * @see \Codeception\Lib\InnerBrowser::grabAttributeFrom() 1110 | */ 1111 | public function grabAttributeFrom($cssOrXpath, $attribute) { 1112 | return $this->scenario->runStep(new \Codeception\Step\Action('grabAttributeFrom', func_get_args())); 1113 | } 1114 | 1115 | 1116 | /** 1117 | * [!] Method is generated. Documentation taken from corresponding module. 1118 | * 1119 | * Finds and returns field and returns it's value. 1120 | * Searches by field name, then by CSS, then by XPath 1121 | * 1122 | * Example: 1123 | * 1124 | * ``` php 1125 | * grabValueFrom('Name'); 1127 | * $name = $I->grabValueFrom('input[name=username]'); 1128 | * $name = $I->grabValueFrom('descendant-or-self::form/descendant::input[@name = 'username']'); 1129 | * ?> 1130 | * ``` 1131 | * 1132 | * @param $field 1133 | * 1134 | * @return mixed 1135 | * @see \Codeception\Lib\InnerBrowser::grabValueFrom() 1136 | */ 1137 | public function grabValueFrom($field) { 1138 | return $this->scenario->runStep(new \Codeception\Step\Action('grabValueFrom', func_get_args())); 1139 | } 1140 | 1141 | 1142 | /** 1143 | * [!] Method is generated. Documentation taken from corresponding module. 1144 | * 1145 | * Sets a cookie. 1146 | * 1147 | * @param $cookie 1148 | * @param $value 1149 | * 1150 | * @return mixed 1151 | * @see \Codeception\Lib\InnerBrowser::setCookie() 1152 | */ 1153 | public function setCookie($name, $val) { 1154 | return $this->scenario->runStep(new \Codeception\Step\Action('setCookie', func_get_args())); 1155 | } 1156 | 1157 | 1158 | /** 1159 | * [!] Method is generated. Documentation taken from corresponding module. 1160 | * 1161 | * Grabs a cookie value. 1162 | * 1163 | * @param $cookie 1164 | * 1165 | * @return mixed 1166 | * @see \Codeception\Lib\InnerBrowser::grabCookie() 1167 | */ 1168 | public function grabCookie($name) { 1169 | return $this->scenario->runStep(new \Codeception\Step\Action('grabCookie', func_get_args())); 1170 | } 1171 | 1172 | 1173 | /** 1174 | * [!] Method is generated. Documentation taken from corresponding module. 1175 | * 1176 | * Checks that cookie is set. 1177 | * 1178 | * @param $cookie 1179 | * 1180 | * @return mixed 1181 | * Conditional Assertion: Test won't be stopped on fail 1182 | * @see \Codeception\Lib\InnerBrowser::seeCookie() 1183 | */ 1184 | public function canSeeCookie($name) { 1185 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCookie', func_get_args())); 1186 | } 1187 | /** 1188 | * [!] Method is generated. Documentation taken from corresponding module. 1189 | * 1190 | * Checks that cookie is set. 1191 | * 1192 | * @param $cookie 1193 | * 1194 | * @return mixed 1195 | * @see \Codeception\Lib\InnerBrowser::seeCookie() 1196 | */ 1197 | public function seeCookie($name) { 1198 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCookie', func_get_args())); 1199 | } 1200 | 1201 | 1202 | /** 1203 | * [!] Method is generated. Documentation taken from corresponding module. 1204 | * 1205 | * Checks that cookie doesn't exist 1206 | * 1207 | * @param $cookie 1208 | * 1209 | * @return mixed 1210 | * Conditional Assertion: Test won't be stopped on fail 1211 | * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() 1212 | */ 1213 | public function cantSeeCookie($name) { 1214 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCookie', func_get_args())); 1215 | } 1216 | /** 1217 | * [!] Method is generated. Documentation taken from corresponding module. 1218 | * 1219 | * Checks that cookie doesn't exist 1220 | * 1221 | * @param $cookie 1222 | * 1223 | * @return mixed 1224 | * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() 1225 | */ 1226 | public function dontSeeCookie($name) { 1227 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCookie', func_get_args())); 1228 | } 1229 | 1230 | 1231 | /** 1232 | * [!] Method is generated. Documentation taken from corresponding module. 1233 | * 1234 | * Unsets cookie 1235 | * 1236 | * @param $cookie 1237 | * 1238 | * @return mixed 1239 | * @see \Codeception\Lib\InnerBrowser::resetCookie() 1240 | */ 1241 | public function resetCookie($name) { 1242 | return $this->scenario->runStep(new \Codeception\Step\Action('resetCookie', func_get_args())); 1243 | } 1244 | 1245 | 1246 | /** 1247 | * [!] Method is generated. Documentation taken from corresponding module. 1248 | * 1249 | * Checks if element exists on a page, matching it by CSS or XPath. 1250 | * You can also specify expected attributes of this element. 1251 | * 1252 | * ``` php 1253 | * seeElement('.error'); 1255 | * $I->seeElement('//form/input[1]'); 1256 | * $I->seeElement('input', ['name' => 'login']); 1257 | * $I->seeElement('input', ['value' => '123456']); 1258 | * ?> 1259 | * ``` 1260 | * 1261 | * @param $selector 1262 | * @param array $attributes 1263 | * @return 1264 | * Conditional Assertion: Test won't be stopped on fail 1265 | * @see \Codeception\Lib\InnerBrowser::seeElement() 1266 | */ 1267 | public function canSeeElement($selector, $attributes = null) { 1268 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeElement', func_get_args())); 1269 | } 1270 | /** 1271 | * [!] Method is generated. Documentation taken from corresponding module. 1272 | * 1273 | * Checks if element exists on a page, matching it by CSS or XPath. 1274 | * You can also specify expected attributes of this element. 1275 | * 1276 | * ``` php 1277 | * seeElement('.error'); 1279 | * $I->seeElement('//form/input[1]'); 1280 | * $I->seeElement('input', ['name' => 'login']); 1281 | * $I->seeElement('input', ['value' => '123456']); 1282 | * ?> 1283 | * ``` 1284 | * 1285 | * @param $selector 1286 | * @param array $attributes 1287 | * @return 1288 | * @see \Codeception\Lib\InnerBrowser::seeElement() 1289 | */ 1290 | public function seeElement($selector, $attributes = null) { 1291 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeElement', func_get_args())); 1292 | } 1293 | 1294 | 1295 | /** 1296 | * [!] Method is generated. Documentation taken from corresponding module. 1297 | * 1298 | * Checks if element does not exist (or is visible) on a page, matching it by CSS or XPath 1299 | * You can also specify expected attributes of this element. 1300 | * 1301 | * Example: 1302 | * 1303 | * ``` php 1304 | * dontSeeElement('.error'); 1306 | * $I->dontSeeElement('//form/input[1]'); 1307 | * $I->dontSeeElement('input', ['name' => 'login']); 1308 | * $I->dontSeeElement('input', ['value' => '123456']); 1309 | * ?> 1310 | * ``` 1311 | * 1312 | * @param $selector 1313 | * Conditional Assertion: Test won't be stopped on fail 1314 | * @see \Codeception\Lib\InnerBrowser::dontSeeElement() 1315 | */ 1316 | public function cantSeeElement($selector, $attributes = null) { 1317 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeElement', func_get_args())); 1318 | } 1319 | /** 1320 | * [!] Method is generated. Documentation taken from corresponding module. 1321 | * 1322 | * Checks if element does not exist (or is visible) on a page, matching it by CSS or XPath 1323 | * You can also specify expected attributes of this element. 1324 | * 1325 | * Example: 1326 | * 1327 | * ``` php 1328 | * dontSeeElement('.error'); 1330 | * $I->dontSeeElement('//form/input[1]'); 1331 | * $I->dontSeeElement('input', ['name' => 'login']); 1332 | * $I->dontSeeElement('input', ['value' => '123456']); 1333 | * ?> 1334 | * ``` 1335 | * 1336 | * @param $selector 1337 | * @see \Codeception\Lib\InnerBrowser::dontSeeElement() 1338 | */ 1339 | public function dontSeeElement($selector, $attributes = null) { 1340 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeElement', func_get_args())); 1341 | } 1342 | 1343 | 1344 | /** 1345 | * [!] Method is generated. Documentation taken from corresponding module. 1346 | * 1347 | * Checks if option is selected in select field. 1348 | * 1349 | * ``` php 1350 | * seeOptionIsSelected('#form input[name=payment]', 'Visa'); 1352 | * ?> 1353 | * ``` 1354 | * 1355 | * @param $selector 1356 | * @param $optionText 1357 | * 1358 | * @return mixed 1359 | * Conditional Assertion: Test won't be stopped on fail 1360 | * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected() 1361 | */ 1362 | public function canSeeOptionIsSelected($select, $optionText) { 1363 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeOptionIsSelected', func_get_args())); 1364 | } 1365 | /** 1366 | * [!] Method is generated. Documentation taken from corresponding module. 1367 | * 1368 | * Checks if option is selected in select field. 1369 | * 1370 | * ``` php 1371 | * seeOptionIsSelected('#form input[name=payment]', 'Visa'); 1373 | * ?> 1374 | * ``` 1375 | * 1376 | * @param $selector 1377 | * @param $optionText 1378 | * 1379 | * @return mixed 1380 | * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected() 1381 | */ 1382 | public function seeOptionIsSelected($select, $optionText) { 1383 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeOptionIsSelected', func_get_args())); 1384 | } 1385 | 1386 | 1387 | /** 1388 | * [!] Method is generated. Documentation taken from corresponding module. 1389 | * 1390 | * Checks if option is not selected in select field. 1391 | * 1392 | * ``` php 1393 | * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); 1395 | * ?> 1396 | * ``` 1397 | * 1398 | * @param $selector 1399 | * @param $optionText 1400 | * 1401 | * @return mixed 1402 | * Conditional Assertion: Test won't be stopped on fail 1403 | * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected() 1404 | */ 1405 | public function cantSeeOptionIsSelected($select, $optionText) { 1406 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeOptionIsSelected', func_get_args())); 1407 | } 1408 | /** 1409 | * [!] Method is generated. Documentation taken from corresponding module. 1410 | * 1411 | * Checks if option is not selected in select field. 1412 | * 1413 | * ``` php 1414 | * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); 1416 | * ?> 1417 | * ``` 1418 | * 1419 | * @param $selector 1420 | * @param $optionText 1421 | * 1422 | * @return mixed 1423 | * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected() 1424 | */ 1425 | public function dontSeeOptionIsSelected($select, $optionText) { 1426 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeOptionIsSelected', func_get_args())); 1427 | } 1428 | 1429 | 1430 | /** 1431 | * [!] Method is generated. Documentation taken from corresponding module. 1432 | * 1433 | * Asserts that current page has 404 response status code. 1434 | * Conditional Assertion: Test won't be stopped on fail 1435 | * @see \Codeception\Lib\InnerBrowser::seePageNotFound() 1436 | */ 1437 | public function canSeePageNotFound() { 1438 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seePageNotFound', func_get_args())); 1439 | } 1440 | /** 1441 | * [!] Method is generated. Documentation taken from corresponding module. 1442 | * 1443 | * Asserts that current page has 404 response status code. 1444 | * @see \Codeception\Lib\InnerBrowser::seePageNotFound() 1445 | */ 1446 | public function seePageNotFound() { 1447 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seePageNotFound', func_get_args())); 1448 | } 1449 | 1450 | 1451 | /** 1452 | * [!] Method is generated. Documentation taken from corresponding module. 1453 | * 1454 | * Checks that response code is equal to value provided. 1455 | * 1456 | * @param $code 1457 | * 1458 | * @return mixed 1459 | * Conditional Assertion: Test won't be stopped on fail 1460 | * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs() 1461 | */ 1462 | public function canSeeResponseCodeIs($code) { 1463 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIs', func_get_args())); 1464 | } 1465 | /** 1466 | * [!] Method is generated. Documentation taken from corresponding module. 1467 | * 1468 | * Checks that response code is equal to value provided. 1469 | * 1470 | * @param $code 1471 | * 1472 | * @return mixed 1473 | * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs() 1474 | */ 1475 | public function seeResponseCodeIs($code) { 1476 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeResponseCodeIs', func_get_args())); 1477 | } 1478 | 1479 | 1480 | /** 1481 | * [!] Method is generated. Documentation taken from corresponding module. 1482 | * 1483 | * Checks that page title contains text. 1484 | * 1485 | * ``` php 1486 | * seeInTitle('Blog - Post #1'); 1488 | * ?> 1489 | * ``` 1490 | * 1491 | * @param $title 1492 | * 1493 | * @return mixed 1494 | * Conditional Assertion: Test won't be stopped on fail 1495 | * @see \Codeception\Lib\InnerBrowser::seeInTitle() 1496 | */ 1497 | public function canSeeInTitle($title) { 1498 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInTitle', func_get_args())); 1499 | } 1500 | /** 1501 | * [!] Method is generated. Documentation taken from corresponding module. 1502 | * 1503 | * Checks that page title contains text. 1504 | * 1505 | * ``` php 1506 | * seeInTitle('Blog - Post #1'); 1508 | * ?> 1509 | * ``` 1510 | * 1511 | * @param $title 1512 | * 1513 | * @return mixed 1514 | * @see \Codeception\Lib\InnerBrowser::seeInTitle() 1515 | */ 1516 | public function seeInTitle($title) { 1517 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInTitle', func_get_args())); 1518 | } 1519 | 1520 | 1521 | /** 1522 | * [!] Method is generated. Documentation taken from corresponding module. 1523 | * 1524 | * Checks that page title does not contain text. 1525 | * 1526 | * @param $title 1527 | * 1528 | * @return mixed 1529 | * Conditional Assertion: Test won't be stopped on fail 1530 | * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle() 1531 | */ 1532 | public function cantSeeInTitle($title) { 1533 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInTitle', func_get_args())); 1534 | } 1535 | /** 1536 | * [!] Method is generated. Documentation taken from corresponding module. 1537 | * 1538 | * Checks that page title does not contain text. 1539 | * 1540 | * @param $title 1541 | * 1542 | * @return mixed 1543 | * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle() 1544 | */ 1545 | public function dontSeeInTitle($title) { 1546 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInTitle', func_get_args())); 1547 | } 1548 | } 1549 | -------------------------------------------------------------------------------- /tests/acceptance/_bootstrap.php: -------------------------------------------------------------------------------- 1 | amHttpAuthenticated('rest_user', 'password123'); 11 | } 12 | 13 | public function _after(ApiGuy $i) 14 | { 15 | } 16 | 17 | // tests 18 | public function getInvalidArtist(ApiGuy $i) 19 | { 20 | $i->wantTo('ensure Getting an invalid Artist id returns a 404 code'); 21 | 22 | $i->sendGET(ApiArtistPage::route('23623623.json')); 23 | $i->seeResponseCodeIs(Response::HTTP_NOT_FOUND); 24 | $i->seeResponseIsJson(); 25 | } 26 | 27 | public function ensureDefaultResponseTypeIsJson(ApiGuy $i) 28 | { 29 | $i->sendGET(ApiArtistPage::route('/1')); 30 | $i->seeResponseCodeIs(Response::HTTP_OK); 31 | $i->seeResponseIsJson(); 32 | } 33 | 34 | public function getValidArtist(ApiGuy $i) 35 | { 36 | foreach ($this->validArtistProvider() as $data) { 37 | $i->sendGET(ApiArtistPage::route('/' . $data[0] . '.json')); 38 | $i->seeResponseCodeIs(Response::HTTP_OK); 39 | $i->seeResponseIsJson(); 40 | 41 | $i->seeResponseContainsJson($data[1]); 42 | } 43 | } 44 | 45 | private function validArtistProvider() 46 | { 47 | return array( 48 | array('1', array( 49 | "name" => "The Beatles", 50 | )), 51 | array('2', array( 52 | "name" => "The Rolling Stones", 53 | )), 54 | ); 55 | } 56 | 57 | public function getArtistsCollection(ApiGuy $i) 58 | { 59 | $i->sendGET(ApiArtistPage::$URL); 60 | $i->seeResponseCodeIs(Response::HTTP_OK); 61 | $i->seeResponseIsJson(); 62 | $i->seeResponseContainsJson(array( 63 | array( 64 | 'id' => 1, 65 | "name" => "The Beatles", 66 | ), 67 | array( 68 | 'id' => 2, 69 | "name" => "The Rolling Stones", 70 | ) 71 | )); 72 | } 73 | 74 | public function getArtistsCollectionWithLimit(ApiGuy $i) 75 | { 76 | $i->sendGET(ApiArtistPage::route('?limit=1')); 77 | $i->seeResponseCodeIs(Response::HTTP_OK); 78 | $i->seeResponseIsJson(); 79 | $i->seeResponseContainsJson(array( 80 | array( 81 | 'id' => 1, 82 | "name" => "The Beatles", 83 | ), 84 | )); 85 | } 86 | 87 | public function getArtistsCollectionWithOffset(ApiGuy $i) 88 | { 89 | $i->sendGET(ApiArtistPage::route('?offset=1')); 90 | $i->seeResponseCodeIs(Response::HTTP_OK); 91 | $i->seeResponseIsJson(); 92 | $i->seeResponseContainsJson(array( 93 | "name" => "The Rolling Stones" 94 | )); 95 | } 96 | 97 | public function getArtistsCollectionWithLimitAndOffset(ApiGuy $i) 98 | { 99 | $i->sendGET(ApiArtistPage::route('?offset=1&limit=3')); 100 | $i->seeResponseCodeIs(Response::HTTP_OK); 101 | $i->seeResponseIsJson(); 102 | $i->seeResponseContainsJson(array( 103 | "name" => "The Rolling Stones" 104 | )); 105 | } 106 | 107 | public function postWithEmptyFieldsReturns400ErrorCode(ApiGuy $i) 108 | { 109 | $i->sendPOST(ApiArtistPage::$URL, array()); 110 | 111 | $i->seeResponseCodeIs(Response::HTTP_BAD_REQUEST); 112 | } 113 | 114 | public function postWithBadFieldsReturns400ErrorCode(ApiGuy $i) 115 | { 116 | $i->sendPOST(ApiArtistPage::$URL, array( 117 | 'bad_field' => 'asdsfsdfds' 118 | )); 119 | 120 | $i->seeResponseCodeIs(Response::HTTP_BAD_REQUEST); 121 | } 122 | 123 | public function goodPostReturns201WithHeader(ApiGuy $i) 124 | { 125 | $name = 'My Artist Name'; 126 | $dob = '1911-11-11 10:10:10'; 127 | 128 | $i->sendPOST(ApiArtistPage::$URL, array( 129 | 'name' => $name, 130 | 'dob' => $dob 131 | )); 132 | 133 | $id = $i->grabFromDatabase('artist', 'id', array( 134 | 'name' => $name 135 | )); 136 | 137 | $i->seeResponseCodeIs(Response::HTTP_CREATED); 138 | $i->canSeeHttpHeader('Location', ApiArtistPage::route('/' . $id, true, true)); 139 | } 140 | 141 | 142 | public function putWithInvalidIdAndInvalidDataReturns400ErrorCode(ApiGuy $i) 143 | { 144 | $i->sendPUT(ApiArtistPage::route('/214234.json'), array( 145 | 'sdfdsfsdf' => 'asfsdfsd', 146 | )); 147 | 148 | $i->seeResponseCodeIs(Response::HTTP_BAD_REQUEST); 149 | } 150 | 151 | public function putWithInvalidIdAndValidDataCreatesNewResourceAndReturns201(ApiGuy $i) 152 | { 153 | $name = 'a new neame'; 154 | $dob = '1911-11-11 10:10:10'; 155 | 156 | $i->sendPUT(ApiArtistPage::route('/3244234234.json'), array( 157 | 'name' => $name, 158 | 'dob' => $dob, 159 | )); 160 | 161 | $id = $i->grabFromDatabase('artist', 'id', array( 162 | 'name' => $name 163 | )); 164 | 165 | $i->seeResponseCodeIs(Response::HTTP_CREATED); 166 | $i->canSeeHttpHeader('Location', ApiArtistPage::route('/' . $id, true, true)); 167 | } 168 | 169 | public function putWithValidIdAndInvalidDataReturns400ErrorCode(ApiGuy $i) 170 | { 171 | $i->sendPUT(ApiArtistPage::route('/2.json'), array( 172 | 'ssdaffsa' => 'cxzvxzc', 173 | )); 174 | 175 | $i->seeResponseCodeIs(Response::HTTP_BAD_REQUEST); 176 | } 177 | 178 | public function putWithValidIdAndValidDataReplacesExistingDataAndReturns204(ApiGuy $i) 179 | { 180 | $name = 'new data'; 181 | 182 | $i->sendPUT(ApiArtistPage::route('/2.json'), array( 183 | 'name' => $name, 184 | )); 185 | 186 | $newName = $i->grabFromDatabase('artist', 'name', array( 187 | 'id' => 2 188 | )); 189 | 190 | $i->seeResponseCodeIs(Response::HTTP_NO_CONTENT); 191 | $i->canSeeHttpHeader('Location', ApiArtistPage::route('/2', true, true)); 192 | $i->assertEquals($name, $newName); 193 | } 194 | 195 | 196 | public function patchWithInvalidIdReturns404(ApiGuy $i) 197 | { 198 | $i->sendPATCH(ApiArtistPage::route('/214234.json'), array( 199 | 'sdfdsfsdf' => 'asfsdfsd', 200 | )); 201 | 202 | $i->seeResponseCodeIs(Response::HTTP_NOT_FOUND); 203 | } 204 | 205 | public function patchWithValidIdAndInvalidDataReturns400ErrorCode(ApiGuy $i) 206 | { 207 | $i->sendPATCH(ApiArtistPage::route('/2.json'), array( 208 | 'sdfdsfsdf' => 'asfsdfsd', 209 | )); 210 | 211 | $i->seeResponseCodeIs(Response::HTTP_BAD_REQUEST); 212 | } 213 | 214 | public function patchWithValidIdAndValidDataReturns204(ApiGuy $i) 215 | { 216 | $originalName = 'The Rolling Stones'; 217 | $dob = new \DateTime('1900-01-01 00:00:00'); 218 | 219 | $i->sendPATCH(ApiArtistPage::route('/2.json'), array( 220 | 'dob' => $dob->format('Y-m-d H:i:s'), 221 | )); 222 | 223 | $newDob = $i->grabFromDatabase('artist', 'dob', array( 224 | 'id' => 2 225 | )); 226 | 227 | $existingName = $i->grabFromDatabase('artist', 'name', array( 228 | 'id' => 2 229 | )); 230 | 231 | $i->seeResponseCodeIs(Response::HTTP_NO_CONTENT); 232 | $i->canSeeHttpHeader('Location', ApiArtistPage::route('/2', true, true)); 233 | $i->assertEquals($dob, new \DateTime($newDob)); 234 | $i->assertEquals($originalName, $existingName); 235 | } 236 | 237 | 238 | public function deleteWithInvalidArtistReturns404(ApiGuy $i) 239 | { 240 | $i->sendDELETE(ApiArtistPage::route('/60000.json')); 241 | 242 | $i->seeResponseCodeIs(Response::HTTP_NOT_FOUND); 243 | } 244 | 245 | public function deleteWithValidArtistReturns204(ApiGuy $i) 246 | { 247 | $i->seeInDatabase('artist', array( 248 | 'id' => 1, 249 | )); 250 | 251 | $i->sendDELETE(ApiArtistPage::route('/1.json')); 252 | 253 | $i->dontSeeInDatabase('artist', array( 254 | 'id' => 1, 255 | )); 256 | 257 | $i->seeResponseCodeIs(Response::HTTP_NO_CONTENT); 258 | } 259 | 260 | 261 | } -------------------------------------------------------------------------------- /tests/api/_bootstrap.php: -------------------------------------------------------------------------------- 1 | scenario->runStep(new \Codeception\Step\Condition('amInPath', func_get_args())); 37 | } 38 | 39 | 40 | /** 41 | * [!] Method is generated. Documentation taken from corresponding module. 42 | * 43 | * Opens a file and stores it's content. 44 | * 45 | * Usage: 46 | * 47 | * ``` php 48 | * openFile('composer.json'); 50 | * $I->seeInThisFile('codeception/codeception'); 51 | * ?> 52 | * ``` 53 | * 54 | * @param $filename 55 | * @see \Codeception\Module\Filesystem::openFile() 56 | */ 57 | public function openFile($filename) { 58 | return $this->scenario->runStep(new \Codeception\Step\Action('openFile', func_get_args())); 59 | } 60 | 61 | 62 | /** 63 | * [!] Method is generated. Documentation taken from corresponding module. 64 | * 65 | * Deletes a file 66 | * 67 | * ``` php 68 | * deleteFile('composer.lock'); 70 | * ?> 71 | * ``` 72 | * 73 | * @param $filename 74 | * @see \Codeception\Module\Filesystem::deleteFile() 75 | */ 76 | public function deleteFile($filename) { 77 | return $this->scenario->runStep(new \Codeception\Step\Action('deleteFile', func_get_args())); 78 | } 79 | 80 | 81 | /** 82 | * [!] Method is generated. Documentation taken from corresponding module. 83 | * 84 | * Deletes directory with all subdirectories 85 | * 86 | * ``` php 87 | * deleteDir('vendor'); 89 | * ?> 90 | * ``` 91 | * 92 | * @param $dirname 93 | * @see \Codeception\Module\Filesystem::deleteDir() 94 | */ 95 | public function deleteDir($dirname) { 96 | return $this->scenario->runStep(new \Codeception\Step\Action('deleteDir', func_get_args())); 97 | } 98 | 99 | 100 | /** 101 | * [!] Method is generated. Documentation taken from corresponding module. 102 | * 103 | * Copies directory with all contents 104 | * 105 | * ``` php 106 | * copyDir('vendor','old_vendor'); 108 | * ?> 109 | * ``` 110 | * 111 | * @param $src 112 | * @param $dst 113 | * @see \Codeception\Module\Filesystem::copyDir() 114 | */ 115 | public function copyDir($src, $dst) { 116 | return $this->scenario->runStep(new \Codeception\Step\Action('copyDir', func_get_args())); 117 | } 118 | 119 | 120 | /** 121 | * [!] Method is generated. Documentation taken from corresponding module. 122 | * 123 | * Checks If opened file has `text` in it. 124 | * 125 | * Usage: 126 | * 127 | * ``` php 128 | * openFile('composer.json'); 130 | * $I->seeInThisFile('codeception/codeception'); 131 | * ?> 132 | * ``` 133 | * 134 | * @param $text 135 | * Conditional Assertion: Test won't be stopped on fail 136 | * @see \Codeception\Module\Filesystem::seeInThisFile() 137 | */ 138 | public function canSeeInThisFile($text) { 139 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInThisFile', func_get_args())); 140 | } 141 | /** 142 | * [!] Method is generated. Documentation taken from corresponding module. 143 | * 144 | * Checks If opened file has `text` in it. 145 | * 146 | * Usage: 147 | * 148 | * ``` php 149 | * openFile('composer.json'); 151 | * $I->seeInThisFile('codeception/codeception'); 152 | * ?> 153 | * ``` 154 | * 155 | * @param $text 156 | * @see \Codeception\Module\Filesystem::seeInThisFile() 157 | */ 158 | public function seeInThisFile($text) { 159 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInThisFile', func_get_args())); 160 | } 161 | 162 | 163 | /** 164 | * [!] Method is generated. Documentation taken from corresponding module. 165 | * 166 | * Checks the strict matching of file contents. 167 | * Unlike `seeInThisFile` will fail if file has something more then expected lines. 168 | * Better to use with HEREDOC strings. 169 | * Matching is done after removing "\r" chars from file content. 170 | * 171 | * ``` php 172 | * openFile('process.pid'); 174 | * $I->seeFileContentsEqual('3192'); 175 | * ?> 176 | * ``` 177 | * 178 | * @param $text 179 | * Conditional Assertion: Test won't be stopped on fail 180 | * @see \Codeception\Module\Filesystem::seeFileContentsEqual() 181 | */ 182 | public function canSeeFileContentsEqual($text) { 183 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileContentsEqual', func_get_args())); 184 | } 185 | /** 186 | * [!] Method is generated. Documentation taken from corresponding module. 187 | * 188 | * Checks the strict matching of file contents. 189 | * Unlike `seeInThisFile` will fail if file has something more then expected lines. 190 | * Better to use with HEREDOC strings. 191 | * Matching is done after removing "\r" chars from file content. 192 | * 193 | * ``` php 194 | * openFile('process.pid'); 196 | * $I->seeFileContentsEqual('3192'); 197 | * ?> 198 | * ``` 199 | * 200 | * @param $text 201 | * @see \Codeception\Module\Filesystem::seeFileContentsEqual() 202 | */ 203 | public function seeFileContentsEqual($text) { 204 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileContentsEqual', func_get_args())); 205 | } 206 | 207 | 208 | /** 209 | * [!] Method is generated. Documentation taken from corresponding module. 210 | * 211 | * Checks If opened file doesn't contain `text` in it 212 | * 213 | * ``` php 214 | * openFile('composer.json'); 216 | * $I->dontSeeInThisFile('codeception/codeception'); 217 | * ?> 218 | * ``` 219 | * 220 | * @param $text 221 | * Conditional Assertion: Test won't be stopped on fail 222 | * @see \Codeception\Module\Filesystem::dontSeeInThisFile() 223 | */ 224 | public function cantSeeInThisFile($text) { 225 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInThisFile', func_get_args())); 226 | } 227 | /** 228 | * [!] Method is generated. Documentation taken from corresponding module. 229 | * 230 | * Checks If opened file doesn't contain `text` in it 231 | * 232 | * ``` php 233 | * openFile('composer.json'); 235 | * $I->dontSeeInThisFile('codeception/codeception'); 236 | * ?> 237 | * ``` 238 | * 239 | * @param $text 240 | * @see \Codeception\Module\Filesystem::dontSeeInThisFile() 241 | */ 242 | public function dontSeeInThisFile($text) { 243 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInThisFile', func_get_args())); 244 | } 245 | 246 | 247 | /** 248 | * [!] Method is generated. Documentation taken from corresponding module. 249 | * 250 | * Deletes a file 251 | * @see \Codeception\Module\Filesystem::deleteThisFile() 252 | */ 253 | public function deleteThisFile() { 254 | return $this->scenario->runStep(new \Codeception\Step\Action('deleteThisFile', func_get_args())); 255 | } 256 | 257 | 258 | /** 259 | * [!] Method is generated. Documentation taken from corresponding module. 260 | * 261 | * Checks if file exists in path. 262 | * Opens a file when it's exists 263 | * 264 | * ``` php 265 | * seeFileFound('UserModel.php','app/models'); 267 | * ?> 268 | * ``` 269 | * 270 | * @param $filename 271 | * @param string $path 272 | * Conditional Assertion: Test won't be stopped on fail 273 | * @see \Codeception\Module\Filesystem::seeFileFound() 274 | */ 275 | public function canSeeFileFound($filename, $path = null) { 276 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); 277 | } 278 | /** 279 | * [!] Method is generated. Documentation taken from corresponding module. 280 | * 281 | * Checks if file exists in path. 282 | * Opens a file when it's exists 283 | * 284 | * ``` php 285 | * seeFileFound('UserModel.php','app/models'); 287 | * ?> 288 | * ``` 289 | * 290 | * @param $filename 291 | * @param string $path 292 | * @see \Codeception\Module\Filesystem::seeFileFound() 293 | */ 294 | public function seeFileFound($filename, $path = null) { 295 | return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); 296 | } 297 | 298 | 299 | /** 300 | * [!] Method is generated. Documentation taken from corresponding module. 301 | * 302 | * Checks if file does not exists in path 303 | * 304 | * @param $filename 305 | * @param string $path 306 | * Conditional Assertion: Test won't be stopped on fail 307 | * @see \Codeception\Module\Filesystem::dontSeeFileFound() 308 | */ 309 | public function cantSeeFileFound($filename, $path = null) { 310 | return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args())); 311 | } 312 | /** 313 | * [!] Method is generated. Documentation taken from corresponding module. 314 | * 315 | * Checks if file does not exists in path 316 | * 317 | * @param $filename 318 | * @param string $path 319 | * @see \Codeception\Module\Filesystem::dontSeeFileFound() 320 | */ 321 | public function dontSeeFileFound($filename, $path = null) { 322 | return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeFileFound', func_get_args())); 323 | } 324 | 325 | 326 | /** 327 | * [!] Method is generated. Documentation taken from corresponding module. 328 | * 329 | * Erases directory contents 330 | * 331 | * ``` php 332 | * cleanDir('logs'); 334 | * ?> 335 | * ``` 336 | * 337 | * @param $dirname 338 | * @see \Codeception\Module\Filesystem::cleanDir() 339 | */ 340 | public function cleanDir($dirname) { 341 | return $this->scenario->runStep(new \Codeception\Step\Action('cleanDir', func_get_args())); 342 | } 343 | 344 | 345 | /** 346 | * [!] Method is generated. Documentation taken from corresponding module. 347 | * 348 | * Saves contents to file 349 | * 350 | * @param $filename 351 | * @param $contents 352 | * @see \Codeception\Module\Filesystem::writeToFile() 353 | */ 354 | public function writeToFile($filename, $contents) { 355 | return $this->scenario->runStep(new \Codeception\Step\Action('writeToFile', func_get_args())); 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /tests/functional/_bootstrap.php: -------------------------------------------------------------------------------- 1 | serviceContainer = $this->getModule('Symfony2')->container; 13 | $this->formFactory = $this->serviceContainer->get('form.factory'); 14 | } 15 | 16 | protected function tearDown() 17 | { 18 | } 19 | 20 | // tests 21 | public function testCanGrabFromServiceContainer() 22 | { 23 | $this->assertInstanceOf( 24 | 'CodeReview\RestBundle\Form\Handler\FormHandler', 25 | $this->serviceContainer->get('code_review.rest_bundle.form.handler.artist_form_handler') 26 | ); 27 | } 28 | 29 | 30 | /** 31 | * @expectedException ErrorException 32 | * @expectedExceptionMessageRegExp /Symfony\\Component\\Form\\FormTypeInterface/ 33 | */ 34 | public function testProcessFormThrowsWhenGivenInvalidFormType() 35 | { 36 | new \CodeReview\RestBundle\Form\Handler\FormHandler( 37 | $this->getMockEntityManager(), 38 | $this->formFactory, 39 | new \StdClass() 40 | ); 41 | } 42 | 43 | /** 44 | * @expectedException Symfony\Component\Form\Exception\LogicException 45 | * @expectedExceptionMessageRegExp /CodeReview\\RestBundle\\Entity\\Artist/ 46 | */ 47 | public function testProcessFormThrowsWhenGivenInvalidObjectForAGivenFormType() 48 | { 49 | $formHandler = new \CodeReview\RestBundle\Form\Handler\FormHandler( 50 | $this->getMockEntityManager(), 51 | $this->formFactory, 52 | new \CodeReview\RestBundle\Form\Type\ArtistType() 53 | ); 54 | 55 | $formHandler->processForm(new \StdClass(), array(), 'POST'); 56 | } 57 | 58 | /** 59 | * @expectedException CodeReview\RestBundle\Exception\InvalidFormException 60 | */ 61 | public function testProcessFormReturnsFormWithErrorsWhenFormIsNotValid() 62 | { 63 | $mockForm = $this->getMock('Symfony\Component\Form\FormInterface'); 64 | $mockForm->expects($this->once()) 65 | ->method('submit'); 66 | 67 | $mockForm->expects($this->once()) 68 | ->method('isValid') 69 | ->will($this->returnValue(false)); 70 | 71 | $formFactory = $this->getMockBuilder('Symfony\Component\Form\FormFactory') 72 | ->disableOriginalConstructor() 73 | ->getMock(); 74 | 75 | $formFactory->expects($this->once()) 76 | ->method('create') 77 | ->will($this->returnValue($mockForm)); 78 | 79 | $formHandler = new \CodeReview\RestBundle\Form\Handler\FormHandler( 80 | $this->getMockEntityManager(), 81 | $formFactory, 82 | new \CodeReview\RestBundle\Form\Type\ArtistType() 83 | ); 84 | 85 | $formHandler->processForm(new \CodeReview\RestBundle\Entity\Artist(), array(), 'POST'); 86 | } 87 | 88 | public function testProcessFormReturnsValidObjectOnSuccess() 89 | { 90 | $formHandler = new \CodeReview\RestBundle\Form\Handler\FormHandler( 91 | $this->getMockEntityManager(), 92 | $this->formFactory, 93 | new \CodeReview\RestBundle\Form\Type\ArtistType() 94 | ); 95 | 96 | $this->assertInstanceOf( 97 | '\CodeReview\RestBundle\Entity\Artist', 98 | $formHandler->processForm(new \CodeReview\RestBundle\Entity\Artist(), array(), "POST") 99 | ); 100 | } 101 | 102 | /** 103 | * @return PHPUnit_Framework_MockObject_MockObject 104 | */ 105 | private function getMockEntityManager() 106 | { 107 | return $this->getMock('Doctrine\Common\Persistence\ObjectManager'); 108 | } 109 | } -------------------------------------------------------------------------------- /tests/unit/CodeReview/RestBundle/Form/Type/ArtistFormTest.php: -------------------------------------------------------------------------------- 1 | 'test', 19 | ); 20 | 21 | $type = new \CodeReview\RestBundle\Form\Type\ArtistType(); 22 | $form = $this->factory->create($type); 23 | 24 | $object = new \CodeReview\RestBundle\Entity\Artist(); 25 | $object->setName($formData['name']); 26 | 27 | // submit the data to the form directly 28 | $form->submit($formData); 29 | 30 | $this->assertTrue($form->isSynchronized()); 31 | $this->assertEquals($object, $form->getData()); 32 | 33 | $view = $form->createView(); 34 | $children = $view->children; 35 | 36 | foreach (array_keys($formData) as $key) { 37 | $this->assertArrayHasKey($key, $children); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /tests/unit/CodeReview/RestBundle/Handler/HandlerTest.php: -------------------------------------------------------------------------------- 1 | getMockRepository(); 17 | 18 | $handler = $this->getHandler($repo); 19 | 20 | $this->assertInstanceOf( 21 | '\CodeReview\RestBundle\Handler\ArtistHandler', 22 | $handler 23 | ); 24 | } 25 | 26 | public function testCanGetWithValidId() 27 | { 28 | $data = array('hi'); 29 | 30 | $repo = $this->getMockRepository(); 31 | $repo->expects($this->once()) 32 | ->method('find') 33 | ->will($this->returnValue($data)) 34 | ; 35 | 36 | $handler = $this->getHandler($repo); 37 | 38 | $this->assertEquals( 39 | $data, 40 | $handler->get(1) 41 | ); 42 | } 43 | 44 | public function testCantGetWithInvalidId() 45 | { 46 | $repo = $this->getMockRepository(); 47 | $repo->expects($this->once()) 48 | ->method('find') 49 | ->will($this->returnValue(null)) 50 | ; 51 | 52 | $handler = $this->getHandler($repo); 53 | 54 | $this->assertNull( 55 | $handler->get(1023032023) 56 | ); 57 | } 58 | 59 | public function testCanGetAll() 60 | { 61 | $data = array(1, 2); 62 | 63 | $repo = $this->getMockRepository(); 64 | $repo->expects($this->once()) 65 | ->method('findBy') 66 | ->will($this->returnValue($data)) 67 | ; 68 | 69 | $handler = $this->getHandler($repo); 70 | 71 | $this->assertEquals( 72 | $data, 73 | $handler->all(1,1) 74 | ); 75 | } 76 | 77 | /** 78 | * @return PHPUnit_Framework_MockObject_MockObject 79 | */ 80 | private function getMockRepository() 81 | { 82 | return $this->getMockBuilder('CodeReview\RestBundle\Repository\ArtistRepository') 83 | ->disableOriginalConstructor() 84 | ->getMock(); 85 | } 86 | 87 | /** 88 | * @param $repo 89 | * @return \CodeReview\RestBundle\Handler\ArtistHandler 90 | */ 91 | private function getHandler($repo) 92 | { 93 | $handler = $this->getMockBuilder('CodeReview\RestBundle\Form\Handler\FormHandler') 94 | ->disableOriginalConstructor() 95 | ->getMock(); 96 | 97 | return new \CodeReview\RestBundle\Handler\ArtistHandler($repo, $handler); 98 | } 99 | } -------------------------------------------------------------------------------- /tests/unit/_bootstrap.php: -------------------------------------------------------------------------------- 1 | 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 | # Redirect to URI without front controller to prevent duplicate content 22 | # (with and without `/app.php`). Only do this redirect on the initial 23 | # rewrite by Apache and not on subsequent cycles. Otherwise we would get an 24 | # endless redirect loop (request -> rewrite to front controller -> 25 | # redirect -> request -> ...). 26 | # So in case you get a "too many redirects" error or you always get redirected 27 | # to the start page because your Apache does not expose the REDIRECT_STATUS 28 | # environment variable, you have 2 choices: 29 | # - disable this feature by commenting the following 2 lines or 30 | # - use Apache >= 2.3.9 and replace all L flags by END flags and remove the 31 | # following RewriteCond (best solution) 32 | RewriteCond %{ENV:REDIRECT_STATUS} ^$ 33 | RewriteRule ^app\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L] 34 | 35 | # If the requested filename exists, simply serve it. 36 | # We only want to let Apache serve files and not directories. 37 | RewriteCond %{REQUEST_FILENAME} -f 38 | RewriteRule .? - [L] 39 | 40 | # Rewrite all other queries to the front controller. 41 | RewriteRule .? %{ENV:BASE}/app.php [L] 42 | 43 | 44 | 45 | 46 | # When mod_rewrite is not available, we instruct a temporary redirect of 47 | # the start page to the front controller explicitly so that the website 48 | # and the generated links can still be used. 49 | RedirectMatch 302 ^/$ /app.php/ 50 | # RedirectTemp cannot be used instead 51 | 52 | 53 | -------------------------------------------------------------------------------- /web/app.php: -------------------------------------------------------------------------------- 1 | unregister(); 14 | $apcLoader->register(true); 15 | */ 16 | 17 | require_once __DIR__.'/../app/AppKernel.php'; 18 | //require_once __DIR__.'/../app/AppCache.php'; 19 | 20 | $kernel = new AppKernel('prod', false); 21 | $kernel->loadClassCache(); 22 | //$kernel = new AppCache($kernel); 23 | 24 | // When using the HttpCache, you need to call the method in your front controller instead of relying on the configuration parameter 25 | //Request::enableHttpMethodParameterOverride(); 26 | $request = Request::createFromGlobals(); 27 | $response = $kernel->handle($request); 28 | $response->send(); 29 | $kernel->terminate($request, $response); 30 | -------------------------------------------------------------------------------- /web/app_codecept_test.php: -------------------------------------------------------------------------------- 1 | loadClassCache(); 33 | $request = Request::createFromGlobals(); 34 | $response = $kernel->handle($request); 35 | $response->send(); 36 | $kernel->terminate($request, $response); 37 | -------------------------------------------------------------------------------- /web/app_dev.php: -------------------------------------------------------------------------------- 1 | loadClassCache(); 27 | $request = Request::createFromGlobals(); 28 | $response = $kernel->handle($request); 29 | $response->send(); 30 | $kernel->terminate($request, $response); 31 | -------------------------------------------------------------------------------- /web/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a6software/symfony2-rest-example/e46b769ccdf16ecc23a4d669c59f26be32da3298/web/apple-touch-icon.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a6software/symfony2-rest-example/e46b769ccdf16ecc23a4d669c59f26be32da3298/web/favicon.ico -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------