diff --git a/.travis.yml b/.travis.yml index 1aac7ae14..db767fcf5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,42 +6,28 @@ cache: directories: - $HOME/.composer/cache -php: - - 5.4 - - 5.5 - - 5.6 - - nightly - - hhvm - -env: - global: - - SYMFONY_DEPRECATIONS_HELPER=weak - matrix: include: - - php: 5.6 + - php: 5.4 env: SYMFONY_VERSION=2.3.* + - php: 5.5 - php: 5.6 - env: SYMFONY_VERSION=2.4.* - - php: 5.6 - env: SYMFONY_VERSION=2.6.* - - php: 5.6 - env: SYMFONY_VERSION=2.7.* + env: SYMFONY_VERSION=2.7.* COVERAGE=true - php: 5.6 - env: SYMFONY_VERSION=2.8.*@dev + env: SYMFONY_VERSION=2.8.* - php: 5.6 - env: SYMFONY_VERSION="3.0.x-dev as 2.8" - allow_failures: - - php: nightly - - env: SYMFONY_VERSION=2.8.*@dev - - env: SYMFONY_VERSION="3.0.x-dev as 2.8" + env: SYMFONY_VERSION=3.0.* DEPENDENCIES=dev COMPOSER_FLAGS="--prefer-stable" + - php: 7.0 + - php: hhvm fast_finish: true before_script: + - if [ "$COVERAGE" != "true" ] && [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then phpenv config-rm xdebug.ini; fi - composer self-update - - if [ "$SYMFONY_VERSION" = "2.8.*@dev" ] || [ "$SYMFONY_VERSION" = "3.0.x-dev as 2.8" ]; then SYMFONY_DEPRECATIONS_HELPER=strict; fi; - - if [ "$SYMFONY_VERSION" != "3.0.x-dev as 2.8" ] && [ "$SYMFONY_VERSION" != "2.7.*" ]; then sed -i "/dunglas\/api-bundle/d;/symfony\/serializer/d" composer.json; fi; + - if [ "$DEPENDENCIES" = "dev" ]; then perl -pi -e 's/^}$/,"minimum-stability":"dev"}/' composer.json; fi; + - if [ "$SYMFONY_VERSION" != "3.0.*" ] && [ "$SYMFONY_VERSION" != "2.8.*" ] && [ "$SYMFONY_VERSION" != "2.7.*" ]; then sed -i "/dunglas\/api-bundle/d;/symfony\/serializer/d" composer.json; fi; - if [ "$SYMFONY_VERSION" != "" ]; then composer require "symfony/symfony:${SYMFONY_VERSION}" --no-update; fi; - - composer install + - composer update $COMPOSER_FLAGS -script: phpunit --coverage-text +script: + - if [ "$COVERAGE" == "true" ]; then phpunit --coverage-text; else phpunit; fi diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index fde2c1282..904dc9005 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -218,10 +218,17 @@ public function getReflectionMethod($controller) } if ($this->container->has($controller)) { - $this->container->enterScope('request'); - $this->container->set('request', new Request(), 'request'); + // BC SF < 3.0 + if (method_exists($this->container, 'enterScope')) { + $this->container->enterScope('request'); + $this->container->set('request', new Request(), 'request'); + } $class = ClassUtils::getRealClass(get_class($this->container->get($controller))); - $this->container->leaveScope('request'); + // BC SF < 3.0 + if (method_exists($this->container, 'enterScope')) { + $this->container->leaveScope('request'); + } + if (!isset($method) && method_exists($class, '__invoke')) { $method = '__invoke'; } diff --git a/Extractor/Handler/FosRestHandler.php b/Extractor/Handler/FosRestHandler.php index 1fe3659e2..7c22694d2 100644 --- a/Extractor/Handler/FosRestHandler.php +++ b/Extractor/Handler/FosRestHandler.php @@ -33,7 +33,7 @@ public function handle(ApiDoc $annotation, array $annotations, Route $route, \Re $requirements = $this->handleRequirements($annot->requirements); $data = array( 'required' => $annot->strict && $annot->nullable === false && $annot->default === null, - 'dataType' => $requirements.($annot->array ? '[]' : ''), + 'dataType' => $requirements.((property_exists($annot, 'map') ? $annot->map : $annot->array) ? '[]' : ''), 'actualType' => $this->inferType($requirements), 'subType' => null, 'description' => $annot->description, @@ -46,19 +46,19 @@ public function handle(ApiDoc $annotation, array $annotations, Route $route, \Re } elseif ($annot instanceof QueryParam) { if ($annot->strict && $annot->nullable === false && $annot->default === null) { $annotation->addRequirement($annot->name, array( - 'requirement' => $this->handleRequirements($annot->requirements).($annot->array ? '[]' : ''), + 'requirement' => $this->handleRequirements($annot->requirements).((property_exists($annot, 'map') ? $annot->map : $annot->array) ? '[]' : ''), 'dataType' => '', 'description' => $annot->description, )); } elseif ($annot->default !== null) { $annotation->addFilter($annot->name, array( - 'requirement' => $this->handleRequirements($annot->requirements).($annot->array ? '[]' : ''), + 'requirement' => $this->handleRequirements($annot->requirements).((property_exists($annot, 'map') ? $annot->map : $annot->array) ? '[]' : ''), 'description' => $annot->description, 'default' => $annot->default, )); } else { $annotation->addFilter($annot->name, array( - 'requirement' => $this->handleRequirements($annot->requirements).($annot->array ? '[]' : ''), + 'requirement' => $this->handleRequirements($annot->requirements).((property_exists($annot, 'map') ? $annot->map : $annot->array) ? '[]' : ''), 'description' => $annot->description, )); } diff --git a/Form/Extension/DescriptionFormTypeExtension.php b/Form/Extension/DescriptionFormTypeExtension.php index 80c218d13..6bcf0415c 100644 --- a/Form/Extension/DescriptionFormTypeExtension.php +++ b/Form/Extension/DescriptionFormTypeExtension.php @@ -11,6 +11,7 @@ namespace Nelmio\ApiDocBundle\Form\Extension; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; @@ -61,6 +62,6 @@ public function configureOptions(OptionsResolver $resolver) */ public function getExtendedType() { - return 'form'; + return LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\FormType'); } } diff --git a/Parser/FormTypeParser.php b/Parser/FormTypeParser.php index dfcc0b8ea..d324d9548 100644 --- a/Parser/FormTypeParser.php +++ b/Parser/FormTypeParser.php @@ -12,6 +12,7 @@ namespace Nelmio\ApiDocBundle\Parser; use Nelmio\ApiDocBundle\DataTypes; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\Form\Exception\FormException; use Symfony\Component\Form\Exception\InvalidArgumentException; use Symfony\Component\Form\Exception\UnexpectedTypeException; @@ -44,6 +45,8 @@ class FormTypeParser implements ParserInterface /** * @var array + * + * @deprecated since 2.12, to be removed in 3.0. Use $extendedMapTypes instead. */ protected $mapTypes = array( 'text' => DataTypes::STRING, @@ -59,6 +62,53 @@ class FormTypeParser implements ParserInterface 'file' => DataTypes::FILE, ); + /** + * @var array + */ + protected $extendedMapTypes = array( + DataTypes::STRING => array( + 'text', + 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'textarea', + 'Symfony\Component\Form\Extension\Core\Type\TextareaType', + 'country', + 'Symfony\Component\Form\Extension\Core\Type\CountryType', + ), + DataTypes::DATE => array( + 'date', + 'Symfony\Component\Form\Extension\Core\Type\DateType', + ), + DataTypes::DATETIME => array( + 'datetime', + 'Symfony\Component\Form\Extension\Core\Type\DatetimeType', + ), + DataTypes::BOOLEAN => array( + 'checkbox', + 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', + ), + DataTypes::TIME => array( + 'time', + 'Symfony\Component\Form\Extension\Core\Type\TimeType', + ), + DataTypes::FLOAT => array( + 'number', + 'Symfony\Component\Form\Extension\Core\Type\NumberType', + ), + DataTypes::INTEGER => array( + 'integer', + 'Symfony\Component\Form\Extension\Core\Type\IntegerType', + ), + DataTypes::ENUM => array( + 'choice', + 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', + ), + DataTypes::FILE => array( + 'file', + 'Symfony\Component\Form\Extension\Core\Type\FileType', + ), + ); + + public function __construct(FormFactoryInterface $formFactory, $entityToChoice) { $this->formFactory = $formFactory; @@ -94,13 +144,28 @@ public function parse(array $item) $type = $item['class']; $options = $item['options']; - if ($this->implementsType($type)) { - $type = $this->getTypeInstance($type); + try { + $form = $this->formFactory->create($type, null, $options); + } + // TODO: find a better exception to catch + catch (\Exception $exception) { + if (!LegacyFormHelper::isLegacy()) { + @trigger_error('Using FormTypeInterface instance with required arguments without defining them as service is deprecated in symfony 2.8 and removed in 3.0.', E_USER_DEPRECATED); + } } - $form = $this->formFactory->create($type, null, $options); + if(!isset($form)) { + if (!LegacyFormHelper::hasBCBreaks() && $this->implementsType($type)) { + $type = $this->getTypeInstance($type); + $form = $this->formFactory->create($type, null, $options); + } else { + throw new \InvalidArgumentException('Unsupported form type class.'); + } + } - $name = array_key_exists('name', $item) ? $item['name'] : $form->getName(); + $name = array_key_exists('name', $item) + ? $item['name'] + : (method_exists($form, 'getBlockPrefix') ? $form->getBlockPrefix() : $form->getName()); if (empty($name)) { return $this->parseForm($form); @@ -129,6 +194,14 @@ public function parse(array $item) ); } + private function getDataType($type) { + foreach ($this->extendedMapTypes as $data => $types) { + if (in_array($type, $types)) { + return $data; + } + } + } + private function parseForm($form) { $parameters = array(); @@ -147,24 +220,27 @@ private function parseForm($form) $typeName = method_exists($type, 'getBlockPrefix') ? $type->getBlockPrefix() : $type->getName(); - if (isset($this->mapTypes[$typeName])) { - $bestType = $this->mapTypes[$typeName]; - $actualType = $bestType; + $dataType = $this->getDataType($typeName); + if (null !== $dataType) { + $actualType = $bestType = $dataType; } elseif ('collection' === $typeName) { - $typeOption = $config->getOption('type'); + // BC sf < 2.8 + $typeOption = $config->hasOption('entry_type') ? $config->getOption('entry_type') : $config->getOption('type'); if (is_object($typeOption)) { $typeOption = method_exists($typeOption, 'getBlockPrefix') ? $typeOption->getBlockPrefix() : $typeOption->getName(); } - if (isset($this->mapTypes[$typeOption])) { - $subType = $this->mapTypes[$typeOption]; + $dataType = $this->getDataType($typeOption); + if (null !== $dataType) { + $subType = $dataType; $actualType = DataTypes::COLLECTION; $bestType = sprintf('array of %ss', $subType); } else { // Embedded form collection - $embbededType = $config->getOption('type'); + // BC sf < 2.8 + $embbededType = $config->hasOption('entry_type') ? $config->getOption('entry_type') : $config->getOption('type'); $subForm = $this->formFactory->create($embbededType, null, $config->getOption('options', array())); $children = $this->parseForm($subForm); $actualType = DataTypes::COLLECTION; @@ -190,7 +266,16 @@ private function parseForm($form) */ $addDefault = false; try { - $subForm = $this->formFactory->create($type, null, $options); + if (LegacyFormHelper::hasBCBreaks()) { + try { + $subForm = $this->formFactory->create(get_class($type), null, $options); + } catch (\Exception $e) { + } + } + if (!isset($subForm)) { + $subForm = $this->formFactory->create($type, null, $options); + } + $subParameters = $this->parseForm($subForm, $name); if (!empty($subParameters)) { @@ -206,7 +291,7 @@ private function parseForm($form) 'default' => null, 'subType' => $subType, 'required' => $config->getRequired(), - 'description' => ($config->getOption('description')) ? $config->getOption('description'):$config->getOption('label'), + 'description' => ($config->getOption('description')) ? $config->getOption('description') : $config->getOption('label'), 'readonly' => $config->getDisabled(), 'children' => $children, ); @@ -224,7 +309,7 @@ private function parseForm($form) 'actualType' => 'string', 'default' => $config->getData(), 'required' => $config->getRequired(), - 'description' => ($config->getOption('description')) ? $config->getOption('description'):$config->getOption('label'), + 'description' => ($config->getOption('description')) ? $config->getOption('description') : $config->getOption('label'), 'readonly' => $config->getDisabled(), ); } @@ -321,20 +406,16 @@ private function getTypeInstance($type) return $refl->newInstance(); } - private function createForm($item, $data = null, array $options = array()) + private function createForm($type, $data = null, array $options = array()) { - if ($this->implementsType($item)) { - $type = $this->getTypeInstance($item); - - return $this->formFactory->create($type, $data, $options); + try { + return $this->formFactory->create($type, null, $options); + } catch(InvalidArgumentException $exception) { } - try { - return $this->formFactory->create($item, $data, $options); - } catch (UnexpectedTypeException $e) { - // nothing - } catch (InvalidArgumentException $e) { - // nothing + if (!LegacyFormHelper::hasBCBreaks() && !isset($form) && $this->implementsType($type)) { + $type = $this->getTypeInstance($type); + return $this->formFactory->create($type, null, $options); } } diff --git a/Parser/ValidationParser.php b/Parser/ValidationParser.php index ec64618e4..e24f201ca 100644 --- a/Parser/ValidationParser.php +++ b/Parser/ValidationParser.php @@ -13,7 +13,8 @@ use Nelmio\ApiDocBundle\DataTypes; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; -use Symfony\Component\Validator\MetadataFactoryInterface; +use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface; +use Symfony\Component\Validator\MetadataFactoryInterface as LegacyMetadataFactoryInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Type; @@ -45,10 +46,13 @@ class ValidationParser implements ParserInterface, PostParserInterface /** * Requires a validation MetadataFactory. * - * @param MetadataFactoryInterface $factory + * @param MetadataFactoryInterface|LegacyMetadataFactoryInterface $factory */ - public function __construct(MetadataFactoryInterface $factory) + public function __construct($factory) { + if (!($factory instanceof MetadataFactoryInterface) && !($factory instanceof LegacyMetadataFactoryInterface)) { + throw new \InvalidArgumentException('Argument 1 of %s constructor must be either an instance of Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface or Symfony\Component\Validator\MetadataFactoryInterface.'); + } $this->factory = $factory; } diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 3b5bfcad6..552584d69 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -37,7 +37,7 @@ - + diff --git a/Tests/Fixtures/Controller/TestController.php b/Tests/Fixtures/Controller/TestController.php index 798a5bb7c..d316dd091 100644 --- a/Tests/Fixtures/Controller/TestController.php +++ b/Tests/Fixtures/Controller/TestController.php @@ -14,6 +14,9 @@ use FOS\RestBundle\Controller\Annotations\QueryParam; use FOS\RestBundle\Controller\Annotations\RequestParam; use Nelmio\ApiDocBundle\Annotation\ApiDoc; +use Nelmio\ApiDocBundle\Tests\Fixtures\DependencyTypePath; +use Nelmio\ApiDocBundle\Tests\Fixtures\RequestParamHelper; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Validator\Constraints\Email; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; @@ -114,7 +117,7 @@ public function yetAnotherAction() * @ApiDoc( * views= { "default", "test" }, * description="create another test", - * input="dependency_type" + * input=DependencyTypePath::TYPE * ) */ public function anotherPostAction() @@ -166,7 +169,7 @@ public function jmsInputTestAction() /** * @ApiDoc( * description="Testing return", - * output="dependency_type" + * output=DependencyTypePath::TYPE * ) */ public function jmsReturnTestAction() @@ -191,7 +194,7 @@ public function zActionWithNullableRequestParamAction() /** * @ApiDoc() - * @RequestParam(name="param1", requirements="string", array=true) + * @RequestParamHelper(name="param1", requirements="string", array=true) */ public function zActionWithArrayRequestParamAction() { diff --git a/Tests/Fixtures/DependencyTypePath.php b/Tests/Fixtures/DependencyTypePath.php new file mode 100644 index 000000000..dba547d78 --- /dev/null +++ b/Tests/Fixtures/DependencyTypePath.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Tests\Fixtures; + +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; + +/** + * This class is used to have dynamic annotations for BC. + * {@see Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController} + * + * @author Ener-Getick + */ +if (LegacyFormHelper::isLegacy()) { + class DependencyTypePath + { + const TYPE = 'dependency_type'; + } +} else { + class DependencyTypePath + { + const TYPE = 'Nelmio\ApiDocBundle\Tests\Fixtures\Form\DependencyType'; + } +} diff --git a/Tests/Fixtures/Form/CollectionType.php b/Tests/Fixtures/Form/CollectionType.php index 1d16dbc43..11c8a4038 100644 --- a/Tests/Fixtures/Form/CollectionType.php +++ b/Tests/Fixtures/Form/CollectionType.php @@ -11,6 +11,7 @@ namespace Nelmio\ApiDocBundle\Tests\Fixtures\Form; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; @@ -22,14 +23,30 @@ class CollectionType extends AbstractType */ public function buildForm(FormBuilderInterface $builder, array $options) { + $collectionType = 'Symfony\Component\Form\Extension\Core\Type\CollectionType'; $builder - ->add('a', 'collection', array('type' => 'text')) - ->add('b', 'collection', array('type' => new TestType())) + ->add('a', LegacyFormHelper::getType($collectionType), array( + LegacyFormHelper::hasBCBreaks() ? 'entry_type' : 'type' => LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\TextType') + )) + ->add('b', LegacyFormHelper::getType($collectionType), array( + LegacyFormHelper::hasBCBreaks() ? 'entry_type' : 'type' => LegacyFormHelper::isLegacy() ? new TestType() : __NAMESPACE__.'\TestType' + )) ; } + /** + * BC SF < 2.8 + * {@inheritdoc} + */ public function getName() { + return $this->getBlockPrefix(); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() { return 'collection_type'; } } diff --git a/Tests/Fixtures/Form/CompoundType.php b/Tests/Fixtures/Form/CompoundType.php index 552666338..4dad4bfac 100644 --- a/Tests/Fixtures/Form/CompoundType.php +++ b/Tests/Fixtures/Form/CompoundType.php @@ -11,6 +11,7 @@ namespace Nelmio\ApiDocBundle\Tests\Fixtures\Form; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -24,18 +25,24 @@ class CompoundType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('sub_form', new SimpleType()) - ->add('a', 'number') + ->add('sub_form', LegacyFormHelper::isLegacy() ? new SimpleType() : __NAMESPACE__.'\SimpleType') + ->add('a', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\NumberType')) ; } /** - * Returns the name of this type. - * - * @return string The name of this type + * BC SF < 2.8 + * {@inheritdoc} */ public function getName() { + return $this->getBlockPrefix(); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() { return ''; } } diff --git a/Tests/Fixtures/Form/DependencyType.php b/Tests/Fixtures/Form/DependencyType.php index 50a741a7d..3ed9d887f 100644 --- a/Tests/Fixtures/Form/DependencyType.php +++ b/Tests/Fixtures/Form/DependencyType.php @@ -54,8 +54,19 @@ public function configureOptions(OptionsResolver $resolver) return; } + /** + * BC SF < 2.8 + * {@inheritdoc} + */ public function getName() { + return $this->getBlockPrefix(); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() { return 'dependency_type'; } } diff --git a/Tests/Fixtures/Form/EntityType.php b/Tests/Fixtures/Form/EntityType.php index 209152626..4ced4fe32 100644 --- a/Tests/Fixtures/Form/EntityType.php +++ b/Tests/Fixtures/Form/EntityType.php @@ -11,6 +11,7 @@ namespace Nelmio\ApiDocBundle\Tests\Fixtures\Form; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; use Symfony\Component\Form\FormBuilderInterface; @@ -43,11 +44,22 @@ public function configureOptions(OptionsResolver $resolver) public function getParent() { - return 'choice'; + return LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\ChoiceType'); } + /** + * BC SF < 2.8 + * {@inheritdoc} + */ public function getName() { + return $this->getBlockPrefix(); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() { return 'entity'; } } diff --git a/Tests/Fixtures/Form/ImprovedTestType.php b/Tests/Fixtures/Form/ImprovedTestType.php index 8c70b0e3d..09a166734 100644 --- a/Tests/Fixtures/Form/ImprovedTestType.php +++ b/Tests/Fixtures/Form/ImprovedTestType.php @@ -11,6 +11,7 @@ namespace Nelmio\ApiDocBundle\Tests\Fixtures\Form; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; use Symfony\Component\Form\FormBuilderInterface; @@ -24,19 +25,35 @@ class ImprovedTestType extends AbstractType */ public function buildForm(FormBuilderInterface $builder, array $options) { + $choiceType = LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\ChoiceType'); + $datetimeType = LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\DateTimeType'); + $dateType = LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\DateType'); + $builder - ->add('dt1', 'datetime', array('widget' => 'single_text', 'description' => 'A nice description')) - ->add('dt2', 'datetime', array('date_format' => 'M/d/y')) - ->add('dt3', 'datetime', array('widget' => 'single_text', 'format' => 'M/d/y H:i:s')) - ->add('dt4', 'datetime', array('date_format' => \IntlDateFormatter::MEDIUM)) - ->add('dt5', 'datetime', array('format' => 'M/d/y H:i:s')) - ->add('d1', 'date', array('format' => \IntlDateFormatter::MEDIUM)) - ->add('d2', 'date', array('format' => 'd-M-y')) - ->add('c1', 'choice', array('choices' => array('m' => 'Male', 'f' => 'Female'))) - ->add('c2', 'choice', array('choices' => array('m' => 'Male', 'f' => 'Female'), 'multiple' => true)) - ->add('c3', 'choice', array('choices' => array())) - ->add('c4', 'choice', array('choices' => array('foo' => 'bar', 'bazgroup' => array('baz' => 'Buzz')))) - ->add('e1', new EntityType(), array('choice_list' => new SimpleChoiceList(array('foo' => 'bar', 'bazgroup' => array('baz' => 'Buzz'))))) + ->add('dt1', $datetimeType, array('widget' => 'single_text', 'description' => 'A nice description')) + ->add('dt2', $datetimeType, array('date_format' => 'M/d/y')) + ->add('dt3', $datetimeType, array('widget' => 'single_text', 'format' => 'M/d/y H:i:s')) + ->add('dt4', $datetimeType, array('date_format' => \IntlDateFormatter::MEDIUM)) + ->add('dt5', $datetimeType, array('format' => 'M/d/y H:i:s')) + ->add('d1', $dateType, array('format' => \IntlDateFormatter::MEDIUM)) + ->add('d2', $dateType, array('format' => 'd-M-y')) + ->add('c1', $choiceType, array_merge( + array('choices' => array('m' => 'Male', 'f' => 'Female')), LegacyFormHelper::isLegacy() ? array() : array('choices_as_values' => true) + )) + ->add('c2', $choiceType, array_merge( + array('choices' => array('m' => 'Male', 'f' => 'Female'), 'multiple' => true), + LegacyFormHelper::isLegacy() ? array() : array('choices_as_values' => true) + )) + ->add('c3', $choiceType, array('choices' => array())) + ->add('c4', $choiceType, array_merge( + array('choices' => array('foo' => 'bar', 'bazgroup' => array('baz' => 'Buzz'))), + LegacyFormHelper::isLegacy() ? array() : array('choices_as_values' => true) + )) + ->add('e1', LegacyFormHelper::isLegacy() ? new EntityType() : __NAMESPACE__.'\EntityType', + LegacyFormHelper::isLegacy() + ? array('choice_list' => new SimpleChoiceList(array('foo' => 'bar', 'bazgroup' => array('baz' => 'Buzz')))) + : array('choices' => array('foo' => 'bar', 'bazgroup' => array('baz' => 'Buzz')), 'choices_as_values' => true) + ) ; } @@ -62,8 +79,19 @@ public function configureOptions(OptionsResolver $resolver) return; } + /** + * BC SF < 2.8 + * {@inheritdoc} + */ public function getName() { + return $this->getBlockPrefix(); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() { return ''; } } diff --git a/Tests/Fixtures/Form/RequireConstructionType.php b/Tests/Fixtures/Form/RequireConstructionType.php index 90032e336..9e4ccc876 100644 --- a/Tests/Fixtures/Form/RequireConstructionType.php +++ b/Tests/Fixtures/Form/RequireConstructionType.php @@ -59,9 +59,20 @@ public function configureOptions(OptionsResolver $resolver) return; } - + + /** + * BC SF < 2.8 + * {@inheritdoc} + */ public function getName() { + return $this->getBlockPrefix(); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() { return 'require_construction_type'; } } diff --git a/Tests/Fixtures/Form/RequiredType.php b/Tests/Fixtures/Form/RequiredType.php index dce97aa44..80e2e3303 100644 --- a/Tests/Fixtures/Form/RequiredType.php +++ b/Tests/Fixtures/Form/RequiredType.php @@ -11,6 +11,7 @@ namespace Nelmio\ApiDocBundle\Tests\Fixtures\Form; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -23,16 +24,22 @@ class RequiredType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { - $builder->add('required_field', 'text', array('required' => true)); + $builder->add('required_field', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\TextType'), array('required' => true)); } /** - * Returns the name of this type. - * - * @return string The name of this type + * BC SF < 2.8 + * {@inheritdoc} */ public function getName() { + return $this->getBlockPrefix(); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() { return ''; } } diff --git a/Tests/Fixtures/Form/SimpleType.php b/Tests/Fixtures/Form/SimpleType.php index 107c5e41d..f07732736 100644 --- a/Tests/Fixtures/Form/SimpleType.php +++ b/Tests/Fixtures/Form/SimpleType.php @@ -11,6 +11,7 @@ namespace Nelmio\ApiDocBundle\Tests\Fixtures\Form; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -23,26 +24,33 @@ class SimpleType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { - $builder->add('a', 'text', array( + $builder->add('a', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\TextType'), array( 'description' => 'Something that describes A.', )) - ->add('b', 'number') - ->add('c', 'choice', array( - 'choices' => array('x' => 'X', 'y' => 'Y', 'z' => 'Z'), + ->add('b', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\NumberType')) + ->add('c', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\ChoiceType'), array_merge( + array('choices' => array('x' => 'X', 'y' => 'Y', 'z' => 'Z')), + LegacyFormHelper::isLegacy() ? array() : array('choices_as_values' => true) )) - ->add('d', 'datetime') - ->add('e', 'date') - ->add('g', 'textarea') + ->add('d', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\DateTimeType')) + ->add('e', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\DateType')) + ->add('g', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\TextareaType')) ; } /** - * Returns the name of this type. - * - * @return string The name of this type + * BC SF < 2.8 + * {@inheritdoc} */ public function getName() { + return $this->getBlockPrefix(); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() { return 'simple'; } } diff --git a/Tests/Fixtures/Form/TestType.php b/Tests/Fixtures/Form/TestType.php index 08853d5b2..bbe75f897 100644 --- a/Tests/Fixtures/Form/TestType.php +++ b/Tests/Fixtures/Form/TestType.php @@ -11,6 +11,7 @@ namespace Nelmio\ApiDocBundle\Tests\Fixtures\Form; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -26,8 +27,8 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder ->add('a', null, array('description' => 'A nice description')) ->add('b') - ->add($builder->create('c', 'checkbox')) - ->add('d','text',array( 'data' => 'DefaultTest')) + ->add($builder->create('c', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\CheckboxType'))) + ->add('d', LegacyFormHelper::getType('Symfony\Component\Form\Extension\Core\Type\TextType'),array( 'data' => 'DefaultTest')) ; } @@ -53,8 +54,19 @@ public function configureOptions(OptionsResolver $resolver) return; } + /** + * BC SF < 2.8 + * {@inheritdoc} + */ public function getName() { + return $this->getBlockPrefix(); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() { return ''; } } diff --git a/Tests/Fixtures/RequestParamHelper.php b/Tests/Fixtures/RequestParamHelper.php new file mode 100644 index 000000000..43d3c3de9 --- /dev/null +++ b/Tests/Fixtures/RequestParamHelper.php @@ -0,0 +1,29 @@ + $value) { + if ($key === 'array') { + if (property_exists($this, 'map')) { + $this->map = $value; + } else { + $this->array = $value; + } + } else { + $this->$key = $value; + } + } + } +} diff --git a/Tests/Fixtures/app/AppKernel.php b/Tests/Fixtures/app/AppKernel.php index 8eaeea305..ec9b7320a 100644 --- a/Tests/Fixtures/app/AppKernel.php +++ b/Tests/Fixtures/app/AppKernel.php @@ -59,6 +59,11 @@ public function registerContainerConfiguration(LoaderInterface $loader) if (class_exists('Dunglas\ApiBundle\DunglasApiBundle')) { $loader->load(__DIR__.'/config/dunglas_api.yml'); } + + // If symfony/framework-bundle > 3.0 + if (!class_exists('Symfony\Bundle\FrameworkBundle\Command\RouterApacheDumperCommand')) { + $loader->load(__DIR__.'/config/twig_assets.yml'); + } } public function serialize() diff --git a/Tests/Fixtures/app/config/routing.yml b/Tests/Fixtures/app/config/routing.yml index 540802ed3..6e8c47943 100644 --- a/Tests/Fixtures/app/config/routing.yml +++ b/Tests/Fixtures/app/config/routing.yml @@ -189,7 +189,7 @@ test_route_update_another_resource: _format: json|xml|html swagger_doc: - resource: @NelmioApiDocBundle/Resources/config/swagger_routing.yml + resource: "@NelmioApiDocBundle/Resources/config/swagger_routing.yml" prefix: /api-docs test_route_23: diff --git a/Tests/Fixtures/app/config/twig_assets.yml b/Tests/Fixtures/app/config/twig_assets.yml new file mode 100644 index 000000000..b538bd8e6 --- /dev/null +++ b/Tests/Fixtures/app/config/twig_assets.yml @@ -0,0 +1,2 @@ +framework: + assets: ~ diff --git a/Tests/Formatter/MarkdownFormatterTest.php b/Tests/Formatter/MarkdownFormatterTest.php index 3a502ad0f..e5f465a18 100644 --- a/Tests/Formatter/MarkdownFormatterTest.php +++ b/Tests/Formatter/MarkdownFormatterTest.php @@ -12,6 +12,7 @@ namespace Nelmio\ApiDocBundle\Tests\Formatter; use Nelmio\ApiDocBundle\Tests\WebTestCase; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; class MarkdownFormatterTest extends WebTestCase { @@ -27,6 +28,9 @@ public function testFormat() $suffix = class_exists('Dunglas\ApiBundle\DunglasApiBundle') ? '' : '-no-dunglas'; $expected = file_get_contents(__DIR__ . '/testFormat-result' . $suffix . '.markdown'); + if (LegacyFormHelper::isLegacy()) { + $expected = str_replace('DependencyType', 'dependency_type', $expected); + } $this->assertEquals($expected, $result . "\n"); } diff --git a/Tests/Formatter/testFormat-result-no-dunglas.markdown b/Tests/Formatter/testFormat-result-no-dunglas.markdown index 3d6413347..c22632cba 100644 --- a/Tests/Formatter/testFormat-result-no-dunglas.markdown +++ b/Tests/Formatter/testFormat-result-no-dunglas.markdown @@ -477,7 +477,7 @@ _create another test_ dependency_type: - * type: object (dependency_type) + * type: object (DependencyType) * required: true dependency_type[a]: @@ -610,7 +610,7 @@ _Testing return_ dependency_type: - * type: object (dependency_type) + * type: object (DependencyType) dependency_type[a]: diff --git a/Tests/Formatter/testFormat-result-no-dunglas.php b/Tests/Formatter/testFormat-result-no-dunglas.php index b9728436a..b43e58abc 100644 --- a/Tests/Formatter/testFormat-result-no-dunglas.php +++ b/Tests/Formatter/testFormat-result-no-dunglas.php @@ -1,5 +1,7 @@ array ( @@ -1174,9 +1176,11 @@ 'readonly' => false, 'description' => '', 'default' => NULL, - 'dataType' => 'object (dependency_type)', + 'dataType' => 'object ('. + (LegacyFormHelper::isLegacy() ? 'dependency_type' : 'DependencyType') + .')', 'actualType' => 'model', - 'subType' => 'dependency_type', + 'subType' => LegacyFormHelper::isLegacy() ? 'dependency_type' : 'Nelmio\ApiDocBundle\Tests\Fixtures\Form\DependencyType', 'children' => array ( 'a' => @@ -1526,9 +1530,11 @@ 'readonly' => false, 'description' => '', 'default' => NULL, - 'dataType' => 'object (dependency_type)', + 'dataType' => 'object ('. + (LegacyFormHelper::isLegacy() ? 'dependency_type' : 'DependencyType') + .')', 'actualType' => 'model', - 'subType' => 'dependency_type', + 'subType' => LegacyFormHelper::isLegacy() ? 'dependency_type' : 'Nelmio\ApiDocBundle\Tests\Fixtures\Form\DependencyType', 'children' => array ( 'a' => diff --git a/Tests/Formatter/testFormat-result.markdown b/Tests/Formatter/testFormat-result.markdown index 7663732a0..068d0f21d 100644 --- a/Tests/Formatter/testFormat-result.markdown +++ b/Tests/Formatter/testFormat-result.markdown @@ -561,7 +561,7 @@ _create another test_ dependency_type: - * type: object (dependency_type) + * type: object (DependencyType) * required: true dependency_type[a]: @@ -694,7 +694,7 @@ _Testing return_ dependency_type: - * type: object (dependency_type) + * type: object (DependencyType) dependency_type[a]: diff --git a/Tests/Formatter/testFormat-result.php b/Tests/Formatter/testFormat-result.php index ea2875765..46c6d87ef 100644 --- a/Tests/Formatter/testFormat-result.php +++ b/Tests/Formatter/testFormat-result.php @@ -1,5 +1,7 @@ array ( @@ -1167,9 +1169,11 @@ 'readonly' => false, 'description' => '', 'default' => NULL, - 'dataType' => 'object (dependency_type)', + 'dataType' => 'object ('. + (LegacyFormHelper::isLegacy() ? 'dependency_type' : 'DependencyType') + .')', 'actualType' => 'model', - 'subType' => 'dependency_type', + 'subType' => LegacyFormHelper::isLegacy() ? 'dependency_type' : 'Nelmio\ApiDocBundle\Tests\Fixtures\Form\DependencyType', 'children' => array ( 'a' => @@ -1518,9 +1522,11 @@ 'readonly' => false, 'description' => '', 'default' => NULL, - 'dataType' => 'object (dependency_type)', + 'dataType' => 'object ('. + (LegacyFormHelper::isLegacy() ? 'dependency_type' : 'DependencyType') + .')', 'actualType' => 'model', - 'subType' => 'dependency_type', + 'subType' => LegacyFormHelper::isLegacy() ? 'dependency_type' : 'Nelmio\ApiDocBundle\Tests\Fixtures\Form\DependencyType', 'children' => array ( 'a' => diff --git a/Tests/Parser/FormTypeParserTest.php b/Tests/Parser/FormTypeParserTest.php index 75b60d672..3a5521e36 100644 --- a/Tests/Parser/FormTypeParserTest.php +++ b/Tests/Parser/FormTypeParserTest.php @@ -15,6 +15,8 @@ use Nelmio\ApiDocBundle\Form\Extension\DescriptionFormTypeExtension; use Nelmio\ApiDocBundle\Parser\FormTypeParser; use Nelmio\ApiDocBundle\Tests\Fixtures; +use Nelmio\ApiDocBundle\Tests\Fixtures\Form\DependencyType; +use Nelmio\ApiDocBundle\Util\LegacyFormHelper; use Symfony\Component\Form\Extension\Core\CoreExtension; use Symfony\Component\Form\Extension\Core\Type\DateTimeType; use Symfony\Component\Form\FormFactory; @@ -28,6 +30,34 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase */ public function testParse($typeName, $expected) { + $resolvedTypeFactory = new ResolvedFormTypeFactory(); + $formFactoryBuilder = new FormFactoryBuilder(); + $formFactoryBuilder->setResolvedTypeFactory($resolvedTypeFactory); + $formFactoryBuilder->addExtension(new CoreExtension()); + $formFactoryBuilder->addTypeExtension(new DescriptionFormTypeExtension()); + $formFactoryBuilder->addType(new DependencyType(array('foo'))); + $formFactory = $formFactoryBuilder->getFormFactory(); + $formTypeParser = new FormTypeParser($formFactory, $entityToChoice = true); + + set_error_handler(array('Nelmio\ApiDocBundle\Tests\WebTestCase', 'handleDeprecation')); + trigger_error('test', E_USER_DEPRECATED); + + $output = $formTypeParser->parse($typeName); + restore_error_handler(); + + $this->assertEquals($expected, $output); + } + + /** + * Checks that we can still use FormType with required arguments without defining them as services. + * @dataProvider dataTestParse + */ + public function testLegacyParse($typeName, $expected) + { + if(LegacyFormHelper::hasBCBreaks()) { + $this->markTestSkipped('Not supported on symfony 3.0.'); + } + $resolvedTypeFactory = new ResolvedFormTypeFactory(); $formFactoryBuilder = new FormFactoryBuilder(); $formFactoryBuilder->setResolvedTypeFactory($resolvedTypeFactory); @@ -55,6 +85,7 @@ public function testParseWithoutEntity($typeName, $expected) $formFactoryBuilder->setResolvedTypeFactory($resolvedTypeFactory); $formFactoryBuilder->addExtension(new CoreExtension()); $formFactoryBuilder->addTypeExtension(new DescriptionFormTypeExtension()); + $formFactoryBuilder->addType(new DependencyType(array('bar'))); $formFactory = $formFactoryBuilder->getFormFactory(); $formTypeParser = new FormTypeParser($formFactory, $entityToChoice = false); @@ -79,14 +110,17 @@ public function dataTestParseWithoutEntity() protected function expectedData($entityToChoice) { - $entityData = array( - 'dataType' => 'choice', - 'actualType' => DataTypes::ENUM, - 'subType' => null, - 'default' => null, - 'required' => true, - 'description' => '', - 'readonly' => false + $entityData = array_merge( + array( + 'dataType' => 'choice', + 'actualType' => DataTypes::ENUM, + 'subType' => null, + 'default' => null, + 'required' => true, + 'description' => '', + 'readonly' => false, + ), + LegacyFormHelper::isLegacy() ? array() : array('format' => '{"foo":"bar","bazgroup":{"baz":"Buzz"}}',) ); return array( diff --git a/Util/LegacyFormHelper.php b/Util/LegacyFormHelper.php new file mode 100644 index 000000000..8b00d729e --- /dev/null +++ b/Util/LegacyFormHelper.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Util; + +/** + * Extracted from FOSUserBundle. + * + * @internal + */ +final class LegacyFormHelper +{ + private static $map = array( + 'Symfony\Component\Form\Extension\Core\Type\FormType' => 'form', + + // Tests + 'Symfony\Component\Form\Extension\Core\Type\CollectionType' => 'collection', + 'Symfony\Component\Form\Extension\Core\Type\NumberType' => 'number', + 'Symfony\Component\Form\Extension\Core\Type\DateTimeType' => 'datetime', + 'Symfony\Component\Form\Extension\Core\Type\DateType' => 'date', + 'Symfony\Component\Form\Extension\Core\Type\ChoiceType' => 'choice', + 'Symfony\Component\Form\Extension\Core\Type\TextType' => 'text', + 'Symfony\Component\Form\Extension\Core\Type\TextareaType' => 'textarea', + 'Symfony\Component\Form\Extension\Core\Type\CheckboxType' => 'checkbox', + 'Nelmio\ApiDocBundle\Tests\Fixtures\Form\DependencyType' => 'dependency_type', + ); + + public static function getType($class) + { + if (!self::isLegacy()) { + return $class; + } + if (!isset(self::$map[$class])) { + throw new \InvalidArgumentException(sprintf('Form type with class "%s" can not be found. Please check for typos or add it to the map in LegacyFormHelper', $class)); + } + + return self::$map[$class]; + } + + public static function isLegacy() + { + return !method_exists('Symfony\Component\Form\AbstractType', 'getBlockPrefix'); + } + + public static function hasBCBreaks() + { + return !method_exists('Symfony\Component\Form\AbstractType', 'setDefaultOptions'); + } + + private function __construct() + { + } + + private function __clone() + { + } +} diff --git a/composer.json b/composer.json index a5306f3b1..ab471f97d 100644 --- a/composer.json +++ b/composer.json @@ -16,9 +16,9 @@ ], "require": { "php": ">=5.3", - "symfony/twig-bundle": "~2.3", - "symfony/framework-bundle": "~2.3", - "symfony/console": "~2.3", + "symfony/twig-bundle": "~2.3|~3.0", + "symfony/framework-bundle": "~2.3|~3.0", + "symfony/console": "~2.3|~3.0", "michelf/php-markdown": "~1.4" }, "conflict": { @@ -27,20 +27,20 @@ "twig/twig": "<1.12" }, "require-dev": { - "symfony/css-selector": "~2.3", - "symfony/browser-kit": "~2.3", - "symfony/validator": "~2.3", - "symfony/yaml": "~2.3", - "symfony/form": "~2.3", - "symfony/finder": "~2.3", - "symfony/serializer": "~2.7", + "symfony/css-selector": "~2.3|~3.0", + "symfony/browser-kit": "~2.3|~3.0", + "symfony/validator": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0", + "symfony/form": "~2.3|~3.0", + "symfony/finder": "~2.3|~3.0", + "symfony/serializer": "~2.7|~3.0", "doctrine/orm": "~2.3", "doctrine/doctrine-bundle": "~1.5", - "friendsofsymfony/rest-bundle": "~1.0", + "friendsofsymfony/rest-bundle": "~1.0|~2.0", "jms/serializer-bundle": ">=0.11", "dunglas/api-bundle": "~1.0@dev", "sensio/framework-extra-bundle": "~3.0", - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0" }, "suggest": { "symfony/form": "For using form definitions as input.",