Merge branch '2.7'

* 2.7: (61 commits)
  fixxed order of usage
  [2.7] [Form] Replaced calls to array_search() by in_array() where is no need to get the index
  removed the last deprecation notice
  [Serializer] Silent deprecation notice
  removed deprecation notice
  [PropertyAccess] Show property path in all exception messages
  added deprecation notice for HttpCache::createEsi()
  added missing deprecation notice when using the form_enctype function
  [Process] Make test AbstractProcessTest::testStartAfterATimeout useful again
  removed non-sense example
  Fixes small typo.
  [Validator] Remove unnecessary include in tests
  [HttpFoundation] minor: clarify Request::getUrlencodedPrefix() regex
  fixed typo
  [Serializer] Use Serializer's LogicException when applicable
  [Serializer] Use autoloader for annotations in tests
  [Validator] fix DOS-style line endings
  Drop useless execution bit
  bumped Symfony version to 2.6.5
  [Serializer] update changelog
  ...

Conflicts:
	CHANGELOG-2.3.md
	CHANGELOG-2.5.md
	CHANGELOG-2.6.md
	src/Symfony/Bridge/Twig/Node/FormEnctypeNode.php
	src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php
	src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php
This commit is contained in:
Fabien Potencier 2015-02-05 07:59:50 +01:00
commit 5c0b5ea821
96 changed files with 1422 additions and 635 deletions

View File

@ -13,19 +13,19 @@ Symfony is the result of the work of many people who made the code better
- Kris Wallsmith (kriswallsmith)
- Christophe Coevoet (stof)
- Nicolas Grekas (nicolas-grekas)
- Pascal Borreli (pborreli)
- Jakub Zalas (jakubzalas)
- Pascal Borreli (pborreli)
- Hugo Hamon (hhamon)
- Karma Dordrak (drak)
- Joseph Bielawski (stloyd)
- Hugo Hamon (hhamon)
- Ryan Weaver (weaverryan)
- Lukas Kahwe Smith (lsmith)
- Romain Neutron (romain)
- Jeremy Mikola (jmikola)
- Jean-François Simon (jfsimon)
- Christian Flothmann (xabbuh)
- Benjamin Eberlei (beberlei)
- Igor Wiedler (igorw)
- Christian Flothmann (xabbuh)
- Martin Hasoň (hason)
- Eriksen Costa (eriksencosta)
- Jonathan Wage (jwage)
@ -33,9 +33,9 @@ Symfony is the result of the work of many people who made the code better
- Alexandre Salomé (alexandresalome)
- William Durand (couac)
- ornicar
- Wouter De Jong (wouterj)
- stealth35 (stealth35)
- Alexander Mols (asm89)
- Wouter De Jong (wouterj)
- Bulat Shakirzyanov (avalanche123)
- Francis Besset (francisbesset)
- Saša Stamenković (umpirsky)
@ -44,15 +44,15 @@ Symfony is the result of the work of many people who made the code better
- Konstantin Kudryashov (everzet)
- Bilal Amarni (bamarni)
- Florin Patan (florinpatan)
- Abdellatif Ait Boudad (aitboudad)
- Eric Clemmons (ericclemmons)
- Sarah Khalil (saro0h)
- Andrej Hudec (pulzarraider)
- Deni
- Henrik Westphal (snc)
- Dariusz Górecki (canni)
- Arnout Boks (aboks)
- Christian Raue
- Sarah Khalil (saro0h)
- Ait Boudad Abdellatif (aitboudad)
- Michel Weimerskirch (mweimerskirch)
- Lee McDermott
- Brandon Turner
@ -69,6 +69,7 @@ Symfony is the result of the work of many people who made the code better
- Kevin Bond (kbond)
- Tim Nagel (merk)
- Brice BERNARD (brikou)
- Kévin Dunglas (dunglas)
- marc.weistroff
- lenar
- Graham Campbell (graham)
@ -81,7 +82,7 @@ Symfony is the result of the work of many people who made the code better
- Jérôme Tamarelle (gromnan)
- Adrien Brault (adrienbrault)
- Fabien Pennequin (fabienpennequin)
- Kévin Dunglas (dunglas)
- Peter Kokot (maastermedia)
- Michal Piotrowski (eventhorizon)
- Gordon Franke (gimler)
- Robert Schönthal (digitalkaoz)
@ -89,13 +90,13 @@ Symfony is the result of the work of many people who made the code better
- Sebastian Hörl (blogsh)
- Daniel Gomes (danielcsgomes)
- Hidenori Goto (hidenorigoto)
- Peter Kokot (maastermedia)
- David Buchmann (dbu)
- Jérémy DERUSSÉ (jderusse)
- Pablo Godel (pgodel)
- Eric GELOEN (gelo)
- Peter Rehm (rpet)
- Jérémie Augustin (jaugustin)
- Rafael Dohms (rdohms)
- Jérémy DERUSSÉ (jderusse)
- Stefano Sala (stefano.sala)
- Tigran Azatyan (tigranazatyan)
- Javier Eguiluz (javier.eguiluz)
@ -113,7 +114,6 @@ Symfony is the result of the work of many people who made the code better
- Rouven Weßling (realityking)
- Dmitrii Chekaliuk (lazyhammer)
- Clément JOBEILI (dator)
- Peter Rehm (rpet)
- Dorian Villet (gnutix)
- Richard Miller (mr_r_miller)
- Arnaud Kleinpeter (nanocom)
@ -130,25 +130,27 @@ Symfony is the result of the work of many people who made the code better
- bronze1man
- sun (sun)
- Larry Garfield (crell)
- Issei Murasawa (issei_m)
- Martin Schuhfuß (usefulthink)
- Thomas Rabaix (rande)
- Matthieu Bontemps (mbontemps)
- Pierre Minnieur (pminnieur)
- fivestar
- Dominique Bongiraud
- Iltar van der Berg
- Leszek Prabucki (l3l0)
- François Zaninotto (fzaninotto)
- Dustin Whittle (dustinwhittle)
- jeff
- Justin Hileman (bobthecow)
- Sven Paulus (subsven)
- Alexander Schwenn (xelaris)
- Lars Strojny (lstrojny)
- Rui Marinho (ruimarinho)
- Julien Brochet (mewt)
- Tugdual Saunier (tucksaun)
- Sergey Linnik (linniksa)
- Marcel Beerta (mazen)
- Iltar van der Berg
- Francois Zaninotto
- Alexander Kotynia (olden)
- Daniel Tschinder
@ -158,6 +160,7 @@ Symfony is the result of the work of many people who made the code better
- Roman Marintšenko (inori)
- Xavier Montaña Carreras (xmontana)
- Michele Orselli (orso)
- Chris Wilkinson (thewilkybarkid)
- Xavier Perez
- Arjen Brouwer (arjenjb)
- Katsuhiro OGAWA
@ -167,7 +170,6 @@ Symfony is the result of the work of many people who made the code better
- Joseph Rouff (rouffj)
- Félix Labrecque (woodspire)
- GordonsLondon
- Issei Murasawa (issei_m)
- Jan Sorgalla (jsor)
- Ray
- Chekote
@ -181,10 +183,13 @@ Symfony is the result of the work of many people who made the code better
- Beau Simensen (simensen)
- Robert Kiss (kepten)
- Kim Hemsø Rasmussen (kimhemsoe)
- Florian Lonqueu-Brochard (florianlb)
- Tom Van Looy (tvlooy)
- Wouter Van Hecke
- Joshua Thijssen
- Peter Kruithof (pkruithof)
- Michael Holm (hollo)
- Warnar Boekkooi (boekkooi)
- Marc Weistroff (futurecat)
- Chris Smith (cs278)
- Florian Klein (docteurklein)
@ -195,10 +200,10 @@ Symfony is the result of the work of many people who made the code better
- Bertrand Zuchuat (garfield-fr)
- Gabor Toth (tgabi333)
- realmfoo
- Chris Wilkinson (thewilkybarkid)
- Thomas Tourlourat (armetiz)
- Andrey Esaulov (andremaha)
- Grégoire Passault (gregwar)
- Mikael Pajunen
- Uwe Jäger (uwej711)
- Aurelijus Valeiša (aurelijus)
- Jan Decavele (jandc)
@ -219,7 +224,6 @@ Symfony is the result of the work of many people who made the code better
- alquerci
- Francesco Levorato
- Vitaliy Zakharov (zakharovvi)
- Florian Lonqueu-Brochard (florianlb)
- Gyula Sallai (salla)
- Inal DJAFAR (inalgnu)
- Christian Gärtner (dagardner)
@ -227,7 +231,6 @@ Symfony is the result of the work of many people who made the code better
- Yaroslav Kiliba
- Sébastien Lavoie (lavoiesl)
- Terje Bråten
- Joshua Thijssen
- Kristen Gilden (kgilden)
- Robbert Klarenbeek (robbertkl)
- Blanchon Vincent (blanchonvincent)
@ -239,6 +242,7 @@ Symfony is the result of the work of many people who made the code better
- Philipp Kräutli (pkraeutli)
- Kirill chEbba Chebunin (chebba)
- Greg Thornton (xdissent)
- Baptiste Clavié (talus)
- Grégoire Paris (greg0ire)
- Costin Bereveanu (schniper)
- Loïc Chardonnet (gnusat)
@ -246,7 +250,6 @@ Symfony is the result of the work of many people who made the code better
- Vyacheslav Salakhutdinov (megazoll)
- Alex Pott
- Tamas Szijarto
- Mikael Pajunen
- Pavel Volokitin (pvolok)
- Endre Fejes
- Tobias Naumann (tna)
@ -254,6 +257,7 @@ Symfony is the result of the work of many people who made the code better
- Shein Alexey
- Joe Lencioni
- Kai
- Lee Rowlands
- Maximilian Reichel (phramz)
- Karoly Negyesi (chx)
- Xavier HAUSHERR
@ -275,6 +279,7 @@ Symfony is the result of the work of many people who made the code better
- Michel Salib (michelsalib)
- geoffrey
- Matthieu Auger (matthieuauger)
- Lorenz Schori
- Jeanmonod David (jeanmonod)
- Jan Schumann
- Niklas Fiekas
@ -287,6 +292,7 @@ Symfony is the result of the work of many people who made the code better
- Konstantin Myakshin (koc)
- vagrant
- Asier Illarramendi (doup)
- Alexander M. Turek (derrabus)
- Chris Sedlmayr (catchamonkey)
- Seb Koelen
- Christoph Mewes (xrstf)
@ -306,7 +312,6 @@ Symfony is the result of the work of many people who made the code better
- Jérôme Macias (jeromemacias)
- Fabian Lange (codingfabian)
- Yoshio HANAWA
- Baptiste Clavié (talus)
- Sebastian Bergmann
- Pablo Díez (pablodip)
- Kevin McBride
@ -337,6 +342,7 @@ Symfony is the result of the work of many people who made the code better
- Nils Adermann (naderman)
- Gábor Fási
- Benjamin Leveque (benji07)
- Javier Spagnoletti (phansys)
- sasezaki
- Dawid Pakuła (zulusx)
- Florian Rey (nervo)
@ -348,6 +354,7 @@ Symfony is the result of the work of many people who made the code better
- Ryan
- Alexander Deruwe (aderuwe)
- François Pluchino (francoispluchino)
- Massimiliano Arione (garak)
- Ivan Rey (ivanrey)
- Marcin Chyłek (songoq)
- Ned Schwartz
@ -356,12 +363,10 @@ Symfony is the result of the work of many people who made the code better
- Zach Badgett (zachbadgett)
- Aurélien Fredouelle
- Pavel Campr (pcampr)
- Alexander Schwenn (xelaris)
- Disquedur
- Geoffrey Tran (geoff)
- Jan Behrens
- Sebastian Krebs
- Lorenz Schori
- Christopher Davis (chrisguitarguy)
- Thomas Lallement (raziel057)
- alcaeus
@ -386,8 +391,6 @@ Symfony is the result of the work of many people who made the code better
- Javier López (loalf)
- Reinier Kip
- Dustin Dobervich (dustin10)
- Warnar Boekkooi
- Alexander M. Turek (derrabus)
- Sebastian Marek (proofek)
- Erkhembayar Gantulga (erheme318)
- David Fuhr
@ -410,6 +413,7 @@ Symfony is the result of the work of many people who made the code better
- Antoine Corcy
- Arturs Vonda
- Sascha Grossenbacher
- Szijarto Tamas
- Ben Davies (bendavies)
- Simon Schick (simonsimcity)
- redstar504
@ -472,7 +476,6 @@ Symfony is the result of the work of many people who made the code better
- Loick Piera (pyrech)
- cgonzalez
- Ben
- Lee Rowlands
- Jayson Xu (superjavason)
- Jaik Dean (jaikdean)
- Harm van Tilborg
@ -490,7 +493,6 @@ Symfony is the result of the work of many people who made the code better
- frost-nzcr4
- Abhoryo
- Fabian Vogler (fabian)
- Javier Spagnoletti (phansys)
- Korvin Szanto
- Maksim Kotlyar (makasim)
- Neil Ferreira
@ -538,7 +540,6 @@ Symfony is the result of the work of many people who made the code better
- Maks
- Gábor Tóth
- Daniel Cestari
- Massimiliano Arione (garak)
- Brunet Laurent (lbrunet)
- Magnus Nordlander (magnusnordlander)
- Mikhail Yurasov (mym)
@ -597,6 +598,7 @@ Symfony is the result of the work of many people who made the code better
- Per Sandström (per)
- Goran Juric
- Laurent Ghirardotti (laurentg)
- Jan Rosier (rosier)
- Lin Clark
- Jeremy David (jeremy.david)
- Troy McCabe
@ -604,6 +606,7 @@ Symfony is the result of the work of many people who made the code better
- Boris Vujicic (boris.vujicic)
- Max Beutel
- Catalin Dan
- nacho
- Piotr Antosik (antek88)
- Artem Lopata
- Marcos Quesada (marcos_quesada)
@ -658,7 +661,6 @@ Symfony is the result of the work of many people who made the code better
- Yannick
- Luc Vieillescazes (iamluc)
- Eduardo García Sanz (coma)
- Szijarto Tamas
- Roy Van Ginneken
- David de Boer (ddeboer)
- Gilles Doge (gido)
@ -673,6 +675,7 @@ Symfony is the result of the work of many people who made the code better
- Derek Lambert
- MightyBranch
- Kacper Gunia (cakper)
- Peter Thompson (petert82)
- Felicitus
- Krzysztof Przybyszewski
- Paul Matthews
@ -700,6 +703,7 @@ Symfony is the result of the work of many people who made the code better
- Aharon Perkel
- Abdul.Mohsen B. A. A
- Benoît Burnichon
- pthompson
- Malaney J. Hill
- Christian Flach (cmfcmf)
- Cédric Girard (enk_)
@ -738,6 +742,7 @@ Symfony is the result of the work of many people who made the code better
- Jason Desrosiers
- m.chwedziak
- Lance McNearney
- Frank Neff (fneff)
- Giorgio Premi
- caponica
- Matt Daum (daum)
@ -784,6 +789,7 @@ Symfony is the result of the work of many people who made the code better
- Klaus Silveira (klaussilveira)
- Thomas Chmielowiec (chmielot)
- Jānis Lukss
- rkerner
- Vladyslav Petrovych
- Matthew J Mucklo
- fdgdfg (psampaz)
@ -832,6 +838,7 @@ Symfony is the result of the work of many people who made the code better
- Przemysław Piechota (kibao)
- Leonid Terentyev (li0n)
- Adam Prager (padam87)
- victoria
- Francisco Facioni (fran6co)
- Iwan van Staveren (istaveren)
- Povilas S. (povilas)
@ -912,6 +919,7 @@ Symfony is the result of the work of many people who made the code better
- Daan van Renterghem
- Bram Van der Sype (brammm)
- Julien Moulin (lizjulien)
- Nikita Nefedov (nikita2206)
- Yannick Warnier (ywarnier)
- Kevin Decherf
- Jason Woods
@ -958,6 +966,7 @@ Symfony is the result of the work of many people who made the code better
- Florian Pfitzer (marmelatze)
- Martin Mayer (martin)
- Grzegorz Łukaszewicz (newicz)
- Richard van Laak (rvanlaak)
- grifx
- Robert Campbell
- Matt Lehner
@ -965,6 +974,7 @@ Symfony is the result of the work of many people who made the code better
- Ruben Kruiswijk
- Michael J
- Berny Cantos
- Joseph Maarek
- Alex Pods
- timaschew
- Ian Phillips
@ -1020,6 +1030,7 @@ Symfony is the result of the work of many people who made the code better
- Pablo Monterde Perez (plebs)
- Jimmy Leger (redpanda)
- Cyrille Jouineau (tuxosaurus)
- Vadim Kharitonov (virtuozzz)
- Yorkie Chadwick (yorkie76)
- Yanick Witschi
- Ondrej Mirtes
@ -1128,6 +1139,7 @@ Symfony is the result of the work of many people who made the code better
- Andreas Forsblom (aforsblo)
- Alaattin Kahramanlar (alaattin)
- Alex Olmos (alexolmos)
- Alain Hippolyte (aloneh)
- Antonio Mansilla (amansilla)
- Juan Ases García (ases)
- Daniel Basten (axhm3a)

View File

@ -59,3 +59,49 @@ Form
}
}
```
Serializer
----------
* The `setCamelizedAttributes()` method of the
`Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer` and
`Symfony\Component\Serializer\Normalizer\PropertyNormalizer` classes is marked
as deprecated in favor of the new NameConverter system.
Before:
```php
$normalizer->setCamelizedAttributes(array('foo_bar', 'bar_foo'));
```
After:
```php
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
$nameConverter = new CamelCaseToSnakeCaseNameConverter(array('fooBar', 'barFoo'));
$normalizer = new GetSetMethodNormalizer(null, $nameConverter);
```
PropertyAccess
--------------
* `UnexpectedTypeException` now expects three constructor arguments: The invalid property value,
the `PropertyPathInterface` object and the current index of the property path.
Before:
```php
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
new UnexpectedTypeException($value, $expectedType);
```
After:
```php
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
new UnexpectedTypeException($value, $path, $pathIndex);
```

View File

@ -244,35 +244,34 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
->assertRaised();
}
public function testValidateUniquenessAfterConsideringMultipleQueryResults()
public function testValidateUniquenessWithValidCustomErrorPath()
{
$constraint = new UniqueEntity(array(
'message' => 'myMessage',
'fields' => array('name'),
'fields' => array('name', 'name2'),
'em' => self::EM_NAME,
'errorPath' => "name2",
));
$entity1 = new SingleIntIdEntity(1, 'Foo');
$entity2 = new SingleIntIdEntity(2, 'Foo');
$entity1 = new DoubleNameEntity(1, 'Foo', "Bar");
$entity2 = new DoubleNameEntity(2, 'Foo', "Bar");
$this->validator->validate($entity1, $constraint);
$this->assertNoViolation();
$this->em->persist($entity1);
$this->em->persist($entity2);
$this->em->flush();
$this->validator->validate($entity1, $constraint);
$this->buildViolation('myMessage')
->atPath('property.path.name')
->setInvalidValue('Foo')
->assertRaised();
$this->context->getViolations()->remove(0);
$this->assertNoViolation();
$this->validator->validate($entity2, $constraint);
$this->buildViolation('myMessage')
->atPath('property.path.name')
->setInvalidValue('Foo')
->atPath('property.path.name2')
->setInvalidValue('Bar')
->assertRaised();
}

View File

@ -133,16 +133,17 @@ class UniqueEntityValidator extends ConstraintValidator
}
$errorPath = null !== $constraint->errorPath ? $constraint->errorPath : $fields[0];
$invalidValue = isset($criteria[$errorPath]) ? $criteria[$errorPath] : $criteria[$fields[0]];
if ($this->context instanceof ExecutionContextInterface) {
$this->context->buildViolation($constraint->message)
->atPath($errorPath)
->setInvalidValue($criteria[$fields[0]])
->setInvalidValue($invalidValue)
->addViolation();
} else {
$this->buildViolation($constraint->message)
->atPath($errorPath)
->setInvalidValue($criteria[$fields[0]])
->setInvalidValue($invalidValue)
->addViolation();
}
}

View File

@ -184,7 +184,7 @@ class ModelChoiceList extends ObjectChoiceList
return array();
}
/**
/*
* This performance optimization reflects a common scenario:
* * A simple select of a model entry.
* * The choice option "expanded" is set to false.
@ -239,7 +239,7 @@ class ModelChoiceList extends ObjectChoiceList
}
if (!$this->loaded) {
/**
/*
* This performance optimization assumes the validation of the respective values will be done by other means.
*
* It correlates with the performance optimization in {@link ModelChoiceList::getChoicesForValues()}

View File

@ -12,6 +12,7 @@
namespace Symfony\Bridge\Twig\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@ -58,7 +59,7 @@ class LintCommand extends Command
$this
->setDescription('Lints a template and outputs encountered errors')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
->addArgument('filename')
->addArgument('filename', InputArgument::IS_ARRAY)
->setHelp(<<<EOF
The <info>%command.name%</info> command lints a template and outputs to STDOUT
the first encountered syntax error.
@ -90,9 +91,9 @@ EOF
return 1;
}
$filename = $input->getArgument('filename');
$filenames = $input->getArgument('filename');
if (!$filename) {
if (0 === count($filenames)) {
if (0 !== ftell(STDIN)) {
throw new \RuntimeException("Please provide a filename or pipe template content to STDIN.");
}
@ -105,14 +106,23 @@ EOF
return $this->display($input, $output, array($this->validate($twig, $template, uniqid('sf_'))));
}
$filesInfo = array();
foreach ($this->findFiles($filename) as $file) {
$filesInfo[] = $this->validate($twig, file_get_contents($file), $file);
}
$filesInfo = $this->getFilesInfo($twig, $filenames);
return $this->display($input, $output, $filesInfo);
}
private function getFilesInfo(\Twig_Environment $twig, array $filenames)
{
$filesInfo = array();
foreach ($filenames as $filename) {
foreach ($this->findFiles($filename) as $file) {
$filesInfo[] = $this->validate($twig, file_get_contents($file), $file);
}
}
return $filesInfo;
}
protected function findFiles($filename)
{
if (is_file($filename)) {

View File

@ -143,7 +143,7 @@ class FormExtension extends \Twig_Extension
public function isSelectedChoice(ChoiceView $choice, $selectedValue)
{
if (is_array($selectedValue)) {
return false !== array_search($choice->value, $selectedValue, true);
return in_array($choice->value, $selectedValue, true);
}
return $choice->value === $selectedValue;

View File

@ -28,7 +28,7 @@ class LintCommandTest extends \PHPUnit_Framework_TestCase
$tester = $this->createCommandTester();
$filename = $this->createFile('{{ foo }}');
$ret = $tester->execute(array('filename' => $filename), array('verbosity' => OutputInterface::VERBOSITY_VERBOSE));
$ret = $tester->execute(array('filename' => array($filename)), array('verbosity' => OutputInterface::VERBOSITY_VERBOSE));
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
$this->assertRegExp('/^OK in /', $tester->getDisplay());
@ -39,7 +39,7 @@ class LintCommandTest extends \PHPUnit_Framework_TestCase
$tester = $this->createCommandTester();
$filename = $this->createFile('{{ foo');
$ret = $tester->execute(array('filename' => $filename));
$ret = $tester->execute(array('filename' => array($filename)));
$this->assertEquals(1, $ret, 'Returns 1 in case of error');
$this->assertRegExp('/^KO in /', $tester->getDisplay());
@ -54,7 +54,7 @@ class LintCommandTest extends \PHPUnit_Framework_TestCase
$filename = $this->createFile('');
unlink($filename);
$ret = $tester->execute(array('filename' => $filename));
$ret = $tester->execute(array('filename' => array($filename)));
}
public function testLintFileCompileTimeException()
@ -62,7 +62,7 @@ class LintCommandTest extends \PHPUnit_Framework_TestCase
$tester = $this->createCommandTester();
$filename = $this->createFile("{{ 2|number_format(2, decimal_point='.', ',') }}");
$ret = $tester->execute(array('filename' => $filename));
$ret = $tester->execute(array('filename' => array($filename)));
$this->assertEquals(1, $ret, 'Returns 1 in case of error');
$this->assertRegExp('/^KO in /', $tester->getDisplay());

View File

@ -14,6 +14,7 @@
<tag name="data_collector" id="dump" template="@Debug/Profiler/dump.html.twig" />
<argument type="service" id="debug.stopwatch" on-invalid="ignore" />
<argument>null</argument><!-- %templating.helper.code.file_link_format% -->
<argument>%kernel.charset%</argument>
</service>
<service id="debug.dump_listener" class="Symfony\Component\HttpKernel\EventListener\DumpListener">

View File

@ -33,7 +33,7 @@ class EventDispatcherDebugCommand extends ContainerAwareCommand
$this
->setName('debug:event-dispatcher')
->setDefinition(array(
new InputArgument('event', InputArgument::OPTIONAL, 'An event name (foo)'),
new InputArgument('event', InputArgument::OPTIONAL, 'An event name'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output description in other formats', 'txt'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'),
))

View File

@ -76,7 +76,7 @@ class ControllerResolver extends BaseControllerResolver
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
}
$controller = new $class();
$controller = $this->instantiateController($class);
if ($controller instanceof ContainerAwareInterface) {
$controller->setContainer($this->container);
}

View File

@ -459,8 +459,13 @@ class Configuration implements ConfigurationInterface
->arrayNode('translator')
->info('translator configuration')
->canBeEnabled()
->fixXmlConfig('fallback')
->children()
->scalarNode('fallback')->defaultValue('en')->end()
->arrayNode('fallbacks')
->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end()
->prototype('scalar')->end()
->defaultValue(array('en'))
->end()
->booleanNode('logging')->defaultValue($this->debug)->end()
->end()
->end()

View File

@ -647,10 +647,7 @@ class FrameworkExtension extends Extension
// Use the "real" translator instead of the identity default
$container->setAlias('translator', 'translator.default');
$translator = $container->findDefinition('translator.default');
if (!is_array($config['fallback'])) {
$config['fallback'] = array($config['fallback']);
}
$translator->addMethodCall('setFallbackLocales', array($config['fallback']));
$translator->addMethodCall('setFallbackLocales', array($config['fallbacks']));
$container->setParameter('translator.logging', $config['logging']);

View File

@ -157,6 +157,9 @@
</xsd:complexType>
<xsd:complexType name="translator">
<xsd:sequence>
<xsd:element name="fallback" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="enabled" type="xsd:boolean" />
<xsd:attribute name="fallback" type="xsd:string" />
<xsd:attribute name="logging" type="xsd:boolean" />

View File

@ -122,7 +122,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
),
'translator' => array(
'enabled' => false,
'fallback' => 'en',
'fallbacks' => array('en'),
'logging' => true,
),
'validation' => array(

View File

@ -0,0 +1,7 @@
<?php
$container->loadFromExtension('framework', array(
'translator' => array(
'fallbacks' => array('en', 'fr'),
),
));

View File

@ -0,0 +1,15 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config secret="s3cr3t">
<framework:translator enabled="true">
<framework:fallback>en</framework:fallback>
<framework:fallback>fr</framework:fallback>
</framework:translator>
</framework:config>
</container>

View File

@ -0,0 +1,3 @@
framework:
translator:
fallbacks: [en, fr]

View File

@ -261,6 +261,14 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertEquals(array('fr'), $calls[0][1][0]);
}
public function testTranslatorMultipleFallbacks()
{
$container = $this->createContainerFromFile('translator_fallbacks');
$calls = $container->getDefinition('translator.default')->getMethodCalls();
$this->assertEquals(array('en', 'fr'), $calls[0][1][0]);
}
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
*/
@ -297,8 +305,10 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertEquals(array(Validation::API_VERSION_2_5_BC), $calls[6][1]);
}
public function testFullyConfiguredValidationService()
public function testLegacyFullyConfiguredValidationService()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
if (!extension_loaded('apc')) {
$this->markTestSkipped('The apc extension is not available.');
}

View File

@ -172,7 +172,7 @@
<!-- Validator -->
<service id="security.validator.user_password" class="%security.validator.user_password.class%">
<tag name="validator.constraint_validator" alias="security.validator.user_password" />
<argument type="service" id="security.context" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.encoder_factory" />
</service>
</services>

View File

@ -151,6 +151,20 @@ class SetAclCommandTest extends WebTestCase
$this->assertTrue($acl2->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity)));
}
protected function setUp()
{
parent::setUp();
$this->deleteTmpDir('Acl');
}
protected function tearDown()
{
parent::tearDown();
$this->deleteTmpDir('Acl');
}
private function getApplication()
{
$kernel = $this->createKernel(array('test_case' => 'Acl'));

View File

@ -106,7 +106,7 @@ class TwigExtension extends Extension
}
unset($config['autoescape_service'], $config['autoescape_service_method']);
$container->setParameter('twig.options', $config);
$container->getDefinition('twig')->replaceArgument(1, $config);
$this->addClassesToCompile(array(
'Twig_Environment',

View File

@ -30,7 +30,7 @@
<services>
<service id="twig" class="%twig.class%">
<argument type="service" id="twig.loader" />
<argument>%twig.options%</argument>
<argument /> <!-- Twig options -->
<call method="addGlobal">
<argument>app</argument>
<argument type="service" id="twig.app_variable" />

View File

@ -35,10 +35,10 @@ class TwigExtensionTest extends TestCase
$this->assertContains('form_div_layout.html.twig', $container->getParameter('twig.form.resources'), '->load() includes default template for form resources');
// Twig options
$options = $container->getParameter('twig.options');
$this->assertEquals(__DIR__.'/twig', $options['cache'], '->load() sets default value for cache option');
$this->assertEquals('UTF-8', $options['charset'], '->load() sets default value for charset option');
$this->assertFalse($options['debug'], '->load() sets default value for debug option');
$options = $container->getDefinition('twig')->getArgument(1);
$this->assertEquals('%kernel.cache_dir%/twig', $options['cache'], '->load() sets default value for cache option');
$this->assertEquals('%kernel.charset%', $options['charset'], '->load() sets default value for charset option');
$this->assertEquals('%kernel.debug%', $options['debug'], '->load() sets default value for debug option');
}
/**
@ -76,7 +76,7 @@ class TwigExtensionTest extends TestCase
}
// Twig options
$options = $container->getParameter('twig.options');
$options = $container->getDefinition('twig')->getArgument(1);
$this->assertTrue($options['auto_reload'], '->load() sets the auto_reload option');
$this->assertTrue($options['autoescape'], '->load() sets the autoescape option');
$this->assertEquals('stdClass', $options['base_template_class'], '->load() sets the base_template_class option');
@ -96,7 +96,7 @@ class TwigExtensionTest extends TestCase
$this->loadFromFile($container, 'customTemplateEscapingGuesser', $format);
$this->compileContainer($container);
$options = $container->getParameter('twig.options');
$options = $container->getDefinition('twig')->getArgument(1);
$this->assertEquals(array(new Reference('my_project.some_bundle.template_escaping_guesser'), 'guess'), $options['autoescape']);
}
@ -110,7 +110,7 @@ class TwigExtensionTest extends TestCase
$this->loadFromFile($container, 'empty', $format);
$this->compileContainer($container);
$options = $container->getParameter('twig.options');
$options = $container->getDefinition('twig')->getArgument(1);
$this->assertEquals('filename', $options['autoescape']);
}

View File

@ -30,7 +30,6 @@
background-color: #f7f7f7;
left: 0;
right: 0;
height: 38px;
margin: 0;
padding: 0 40px 0 0;
z-index: 6000000;
@ -88,6 +87,7 @@
color: #2f2f2f;
display: block;
min-height: 28px;
border-bottom: 1px solid #e4e4e4;
border-right: 1px solid #e4e4e4;
padding: 0;
float: left;
@ -150,10 +150,6 @@
border-radius: 4px 4px 0 0;
}
.sf-toolbarreset > div:last-of-type .sf-toolbar-info {
right: -1px;
}
.sf-toolbar-block .sf-toolbar-info:empty {
visibility: hidden;
}

View File

@ -31,6 +31,30 @@
}
Sfjs.renderAjaxRequests();
/* Handle toolbar-info position */
var toolbarBlocks = document.getElementsByClassName('sf-toolbar-block');
for (var i = 0; i < toolbarBlocks.length; i += 1) {
toolbarBlocks[i].onmouseover = function () {
var toolbarInfo = this.getElementsByClassName('sf-toolbar-info')[0];
var pageWidth = document.body.clientWidth;
var elementWidth = toolbarInfo.offsetWidth;
var leftValue = (elementWidth + this.offsetLeft) - pageWidth;
var rightValue = (elementWidth + (pageWidth - this.offsetLeft)) - pageWidth;
/* Reset right and left value, useful on window resize */
toolbarInfo.style.right = '';
toolbarInfo.style.left = '';
if (leftValue > 0 && rightValue > 0) {
toolbarInfo.style.right = (rightValue * -1) + 'px';
} else if (leftValue < 0) {
toolbarInfo.style.left = 0;
} else {
toolbarInfo.style.right = '-1px';
}
};
}
},
function(xhr) {
if (xhr.status !== 0) {

View File

@ -164,7 +164,7 @@ class TextDescriptor extends Descriptor
}
$this->writeText("<comment>Usage:</comment>\n", $options);
$this->writeText(" [options] command [arguments]\n\n", $options);
$this->writeText(" command [options] [arguments]\n\n", $options);
$this->writeText('<comment>Options:</comment>', $options);
$inputOptions = $application->getDefinition()->getOptions();

View File

@ -1,7 +1,7 @@
<info>Console Tool</info>
<comment>Usage:</comment>
[options] command [arguments]
command [options] [arguments]
<comment>Options:</comment>
<info>--help</info> (-h) Display this help message

View File

@ -1,7 +1,7 @@
<info>My Symfony application</info> version <comment>v1.0</comment>
<comment>Usage:</comment>
[options] command [arguments]
command [options] [arguments]
<comment>Options:</comment>
<info>--help</info> (-h) Display this help message

View File

@ -1,7 +1,7 @@
<info>Console Tool</info>
<comment>Usage:</comment>
[options] command [arguments]
command [options] [arguments]
<comment>Options:</comment>
<info>--help</info> (-h) Display this help message

View File

@ -1,7 +1,7 @@
<info>Console Tool</info>
<comment>Usage:</comment>
[options] command [arguments]
command [options] [arguments]
<comment>Options:</comment>
<info>--help</info> (-h) Display this help message

View File

@ -1,7 +1,7 @@
Console Tool
Usage:
[options] command [arguments]
command [options] [arguments]
Options:
--help (-h) Display this help message

0
src/Symfony/Component/DomCrawler/Crawler.php Executable file → Normal file
View File

View File

@ -216,7 +216,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
$this->dispatcher->removeListener($eventName, $listener);
$info = $this->getListenerInfo($listener, $eventName);
$name = isset($info['class']) ? $info['class'] : $info['type'];
$this->dispatcher->addListener($eventName, new WrappedListener($listener, $name, $this->stopwatch));
$this->dispatcher->addListener($eventName, new WrappedListener($listener, $name, $this->stopwatch, $this));
}
}

View File

@ -25,12 +25,14 @@ class WrappedListener
private $called;
private $stoppedPropagation;
private $stopwatch;
private $dispatcher;
public function __construct($listener, $name, Stopwatch $stopwatch)
public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
{
$this->listener = $listener;
$this->name = $name;
$this->stopwatch = $stopwatch;
$this->dispatcher = $dispatcher;
$this->called = false;
$this->stoppedPropagation = false;
}
@ -56,7 +58,7 @@ class WrappedListener
$e = $this->stopwatch->start($this->name, 'event_listener');
call_user_func($this->listener, $event, $eventName, $dispatcher);
call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher);
if ($e->isStarted()) {
$e->stop();

View File

@ -86,6 +86,20 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(), $tdispatcher->getNotCalledListeners());
}
public function testGetCalledListenersNested()
{
$tdispatcher = null;
$dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
$dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) {
$tdispatcher = $dispatcher;
$dispatcher->dispatch('bar');
});
$dispatcher->addListener('bar', function (Event $event) {});
$dispatcher->dispatch('foo');
$this->assertSame($dispatcher, $tdispatcher);
$this->assertCount(2, $dispatcher->getCalledListeners());
}
public function testLogger()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');

View File

@ -389,7 +389,7 @@ class ChoiceList implements ChoiceListInterface
*/
protected function isPreferred($choice, array $preferredChoices)
{
return false !== array_search($choice, $preferredChoices, true);
return in_array($choice, $preferredChoices, true);
}
/**

View File

@ -110,7 +110,7 @@ class ChoiceType extends AbstractType
// avoid making the type check inside the closure.
if ($options['multiple']) {
$view->vars['is_selected'] = function ($choice, array $values) {
return false !== array_search($choice, $values, true);
return in_array($choice, $values, true);
};
} else {
$view->vars['is_selected'] = function ($choice, $value) {

View File

@ -1,19 +1,19 @@
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="28">
<source>This form should not contain extra fields.</source>
<target>Тази форма не трябва да съдържа допълнителни полета.</target>
</trans-unit>
<trans-unit id="29">
<source>The uploaded file was too large. Please try to upload a smaller file.</source>
<target>Каченият файл е твърде голям. Моля, опитайте да качите по-малък файл.</target>
</trans-unit>
<trans-unit id="30">
<source>The CSRF token is invalid. Please try to resubmit the form.</source>
<target>Невалиден CSRF токен. Моля, опитайте да изпратите формата отново.</target>
</trans-unit>
</body>
</file>
</xliff>
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="28">
<source>This form should not contain extra fields.</source>
<target>Тази форма не трябва да съдържа допълнителни полета.</target>
</trans-unit>
<trans-unit id="29">
<source>The uploaded file was too large. Please try to upload a smaller file.</source>
<target>Каченият файл е твърде голям. Моля, опитайте да качите по-малък файл.</target>
</trans-unit>
<trans-unit id="30">
<source>The CSRF token is invalid. Please try to resubmit the form.</source>
<target>Невалиден CSRF токен. Моля, опитайте да изпратите формата отново.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -1951,7 +1951,7 @@ class Request
$len = strlen($prefix);
if (preg_match("#^(%[[:xdigit:]]{2}|.){{$len}}#", $string, $match)) {
if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) {
return $match[0];
}

View File

@ -16,7 +16,6 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
*
* @see http://php.net/sessionhandler
*/
class NativeSessionHandler extends \SessionHandler
{
}

View File

@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Symfony\Component\VarDumper\Dumper\DataDumperInterface;
@ -31,11 +32,13 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
private $clonesCount = 0;
private $clonesIndex = 0;
private $rootRefs;
private $charset;
public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null)
public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, $charset = null)
{
$this->stopwatch = $stopwatch;
$this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
$this->charset = $charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8';
// All clones share these properties by reference:
$this->rootRefs = array(
@ -93,7 +96,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$fileExcerpt = array();
for ($i = max($line - 3, 1), $max = min($line + 3, count($src)); $i <= $max; ++$i) {
$fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.htmlspecialchars($src[$i - 1]).'</code></li>';
$fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.$this->htmlEncode($src[$i - 1]).'</code></li>';
}
$fileExcerpt = '<ol start="'.max($line - 3, 1).'">'.implode("\n", $fileExcerpt).'</ol>';
@ -153,7 +156,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$data = fopen('php://memory', 'r+b');
if ('html' === $format) {
$dumper = new HtmlDumper($data);
$dumper = new HtmlDumper($data, $this->charset);
} else {
throw new \InvalidArgumentException(sprintf('Invalid dump format: %s', $format));
}
@ -190,10 +193,9 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
}
if ('cli' !== PHP_SAPI && stripos($h[$i], 'html')) {
echo '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">';
$dumper = new HtmlDumper('php://output');
$dumper = new HtmlDumper('php://output', $this->charset);
} else {
$dumper = new CliDumper('php://output');
$dumper = new CliDumper('php://output', $this->charset);
$dumper->setColors(false);
}
@ -201,8 +203,8 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$this->data[$i] = null;
if ($dumper instanceof HtmlDumper) {
$dump['name'] = htmlspecialchars($dump['name'], ENT_QUOTES, 'UTF-8');
$dump['file'] = htmlspecialchars($dump['file'], ENT_QUOTES, 'UTF-8');
$dump['name'] = $this->htmlEncode($dump['name']);
$dump['file'] = $this->htmlEncode($dump['file']);
if ('' !== $dump['file']) {
if ($this->fileLinkFormat) {
$link = strtr($this->fileLinkFormat, array('%f' => $dump['file'], '%l' => $dump['line']));
@ -222,4 +224,18 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$this->dataCount = 0;
}
}
private function htmlEncode($s)
{
$html = '';
$dumper = new HtmlDumper(function ($line) use (&$html) {$html .= $line;}, $this->charset);
$dumper->setDumpHeader('');
$dumper->setDumpBoundaries('', '');
$cloner = new VarCloner();
$dumper->dump($cloner->cloneVar($s));
return substr(strip_tags($html), 1, -1);
}
}

View File

@ -13,7 +13,7 @@ namespace Symfony\Component\HttpKernel\Exception;
trigger_error('The '.__NAMESPACE__.'\FatalErrorException class is deprecated since version 2.3 and will be removed in 3.0. Use the Symfony\Component\Debug\Exception\FatalErrorException class instead.', E_USER_DEPRECATED);
/**
/*
* Fatal Error Exception.
*
* @author Konstanton Myakshin <koc-dp@yandex.ru>

View File

@ -13,7 +13,7 @@ namespace Symfony\Component\HttpKernel\Exception;
trigger_error('The '.__NAMESPACE__.'\FlattenException class is deprecated since version 2.3 and will be removed in 3.0. Use the Symfony\Component\Debug\Exception\FlattenException class instead.', E_USER_DEPRECATED);
/**
/*
* FlattenException wraps a PHP Exception to be able to serialize it.
*
* Basically, this class removes all objects from the trace.

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\HttpKernel\Tests;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Config\EnvParametersResource;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
@ -113,7 +114,7 @@ class KernelTest extends \PHPUnit_Framework_TestCase
public function testEnvParametersResourceIsAdded()
{
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
$container = new ContainerBuilder();
$kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest')
->disableOriginalConstructor()
->setMethods(array('getContainerBuilder', 'prepareContainer', 'getCacheDir', 'getLogDir'))
@ -130,14 +131,21 @@ class KernelTest extends \PHPUnit_Framework_TestCase
$kernel->expects($this->any())
->method('getLogDir')
->will($this->returnValue(sys_get_temp_dir()));
$container->expects($this->once())
->method('addResource')
->with(new EnvParametersResource('SYMFONY__'));
$reflection = new \ReflectionClass(get_class($kernel));
$method = $reflection->getMethod('buildContainer');
$method->setAccessible(true);
$method->invoke($kernel);
$found = false;
foreach ($container->getResources() as $resource) {
if ($resource instanceof EnvParametersResource) {
$found = true;
break;
}
}
$this->assertTrue($found);
}
public function testBootKernelSeveralTimesOnlyInitializesBundlesOnce()

View File

@ -504,7 +504,19 @@ class OptionsResolver2Dot6Test extends \PHPUnit_Framework_TestCase
*/
public function testResolveFailsIfInvalidType()
{
$this->resolver->setDefault('foo', 42);
$this->resolver->setDefined('foo');
$this->resolver->setAllowedTypes('foo', 'string');
$this->resolver->resolve(array('foo' => 42));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
* @expectedExceptionMessage The option "foo" with value null is expected to be of type "string", but is of type "NULL".
*/
public function testResolveFailsIfInvalidTypeIsNull()
{
$this->resolver->setDefault('foo', null);
$this->resolver->setAllowedTypes('foo', 'string');
$this->resolver->resolve();
@ -675,7 +687,19 @@ class OptionsResolver2Dot6Test extends \PHPUnit_Framework_TestCase
*/
public function testResolveFailsIfInvalidValue()
{
$this->resolver->setDefault('foo', 42);
$this->resolver->setDefined('foo');
$this->resolver->setAllowedValues('foo', 'bar');
$this->resolver->resolve(array('foo' => 42));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
* @expectedExceptionMessage The option "foo" with value null is invalid. Accepted values are: "bar".
*/
public function testResolveFailsIfInvalidValueIsNull()
{
$this->resolver->setDefault('foo', null);
$this->resolver->setAllowedValues('foo', 'bar');
$this->resolver->resolve();

View File

@ -841,13 +841,13 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testStartAfterATimeout()
{
$process = $this->getProcess('php -r "$n = 1000; while ($n--) {echo \'\'; usleep(1000); }"');
$process = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 1000; while ($n--) {echo \'\'; usleep(1000); }')));
$process->setTimeout(0.1);
try {
$process->run();
$this->fail('An exception should have been raised.');
} catch (\Exception $e) {
$this->fail('A RuntimeException should have been raised.');
} catch (RuntimeException $e) {
}
$process->start();
usleep(1000);

View File

@ -1,6 +1,12 @@
CHANGELOG
=========
2.7.0
------
* `UnexpectedTypeException` now expects three constructor arguments: The invalid property value,
the `PropertyPathInterface` object and the current index of the property path.
2.5.0
------

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\PropertyAccess\Exception;
use Symfony\Component\PropertyAccess\PropertyPathInterface;
/**
* Thrown when a value does not match an expected type.
*
@ -18,8 +20,31 @@ namespace Symfony\Component\PropertyAccess\Exception;
*/
class UnexpectedTypeException extends RuntimeException
{
public function __construct($value, $expectedType)
/**
* @param mixed $value The unexpected value found while traversing property path
* @param PropertyPathInterface $path The property path
* @param int $pathIndex The property path index when the unexpected value was found
*/
public function __construct($value, $path, $pathIndex = null)
{
parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value)));
if (func_num_args() === 3 && $path instanceof PropertyPathInterface) {
$message = sprintf(
'PropertyAccessor requires a graph of objects or arrays to operate on, '.
'but it found type "%s" while trying to traverse path "%s" at property "%s".',
gettype($value),
(string) $path,
$path->getElement($pathIndex)
);
} else {
trigger_error('The '.__CLASS__.' constructor now expects 3 arguments: the invalid property value, the '.__NAMESPACE__.'\PropertyPathInterface object and the current index of the property path.', E_USER_DEPRECATED);
$message = sprintf(
'Expected argument of type "%s", "%s" given',
$path,
is_object($value) ? get_class($value) : gettype($value)
);
}
parent::__construct($message);
}
}

View File

@ -97,7 +97,7 @@ class PropertyAccessor implements PropertyAccessorInterface
if ($overwrite) {
if (!is_object($objectOrArray) && !is_array($objectOrArray)) {
throw new UnexpectedTypeException($objectOrArray, 'object or array');
throw new UnexpectedTypeException($objectOrArray, $propertyPath, $i);
}
$property = $propertyPath->getElement($i);
@ -221,7 +221,7 @@ class PropertyAccessor implements PropertyAccessorInterface
for ($i = 0; $i < $lastIndex; ++$i) {
if (!is_object($objectOrArray) && !is_array($objectOrArray)) {
throw new UnexpectedTypeException($objectOrArray, 'object or array');
throw new UnexpectedTypeException($objectOrArray, $propertyPath, $i);
}
$property = $propertyPath->getElement($i);

View File

@ -11,9 +11,9 @@
namespace Symfony\Component\PropertyAccess;
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException;
use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException;
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
/**
* Default implementation of {@link PropertyPathInterface}.
@ -70,7 +70,7 @@ class PropertyPath implements \IteratorAggregate, PropertyPathInterface
*
* @param PropertyPath|string $propertyPath The property path as string or instance
*
* @throws UnexpectedTypeException If the given path is not a string
* @throws InvalidArgumentException If the given path is not a string
* @throws InvalidPropertyPathException If the syntax of the property path is not valid
*/
public function __construct($propertyPath)
@ -87,7 +87,12 @@ class PropertyPath implements \IteratorAggregate, PropertyPathInterface
return;
}
if (!is_string($propertyPath)) {
throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPath');
throw new InvalidArgumentException(sprintf(
'The property path constructor needs a string or an instance of '.
'"Symfony\Component\PropertyAccess\PropertyPath". '.
'Got: "%s"',
is_object($propertyPath) ? get_class($propertyPath) : gettype($propertyPath)
));
}
if ('' === $propertyPath) {

View File

@ -139,6 +139,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "string" while trying to traverse path "foobar" at property "foobar".
*/
public function testGetValueThrowsExceptionIfNotObjectOrArray()
{
@ -147,6 +148,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "NULL" while trying to traverse path "foobar" at property "foobar".
*/
public function testGetValueThrowsExceptionIfNull()
{
@ -155,12 +157,22 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "string" while trying to traverse path "foobar" at property "foobar".
*/
public function testGetValueThrowsExceptionIfEmpty()
{
$this->propertyAccessor->getValue('', 'foobar');
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "NULL" while trying to traverse path "foobar.baz" at property "baz".
*/
public function testGetValueNestedExceptionMessage()
{
$this->propertyAccessor->getValue((object) array('foobar' => null), 'foobar.baz');
}
/**
* @dataProvider getValidPropertyPaths
*/
@ -249,6 +261,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "string" while trying to traverse path "foobar" at property "foobar".
*/
public function testSetValueThrowsExceptionIfNotObjectOrArray()
{
@ -259,6 +272,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "NULL" while trying to traverse path "foobar" at property "foobar".
*/
public function testSetValueThrowsExceptionIfNull()
{
@ -269,6 +283,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "string" while trying to traverse path "foobar" at property "foobar".
*/
public function testSetValueThrowsExceptionIfEmpty()
{
@ -277,6 +292,17 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
$this->propertyAccessor->setValue($value, 'foobar', 'bam');
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "NULL" while trying to traverse path "foobar.baz" at property "baz".
*/
public function testSetValueNestedExceptionMessage()
{
$value = (object) array('foobar' => null);
$this->propertyAccessor->setValue($value, 'foobar.baz', 'bam');
}
public function testGetValueWhenArrayValueIsNull()
{
$this->propertyAccessor = new PropertyAccessor(false, true);

View File

@ -69,7 +69,7 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
*/
public function testPathCannotBeNull()
{
@ -77,7 +77,7 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
*/
public function testPathCannotBeFalse()
{

View File

@ -225,7 +225,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
$routeHost = '';
foreach ($hostTokens as $token) {
if ('variable' === $token[0]) {
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#', $mergedParams[$token[3]])) {
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#i', $mergedParams[$token[3]])) {
$message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given) to generate a corresponding URL.', $token[3], $name, $token[2], $mergedParams[$token[3]]);
if ($this->strictRequirements) {

View File

@ -46,7 +46,7 @@ class RouteCompiler implements RouteCompilerInterface
$result = self::compilePattern($route, $host, true);
$hostVariables = $result['variables'];
$variables = array_merge($variables, $hostVariables);
$variables = $hostVariables;
$hostTokens = $result['tokens'];
$hostRegex = $result['regex'];
@ -163,7 +163,7 @@ class RouteCompiler implements RouteCompilerInterface
return array(
'staticPrefix' => 'text' === $tokens[0][0] ? $tokens[0][1] : '',
'regex' => self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s',
'regex' => self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s'.($isHost ? 'i' : ''),
'tokens' => array_reverse($tokens),
'variables' => $variables,
);

View File

@ -197,7 +197,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
$host = $this->context->getHost();
if (preg_match('#^a\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
// route1
if ($pathinfo === '/route1') {
return array('_route' => 'route1');
@ -210,7 +210,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
if (preg_match('#^b\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^b\\.example\\.com$#si', $host, $hostMatches)) {
// route3
if ($pathinfo === '/c2/route3') {
return array('_route' => 'route3');
@ -218,7 +218,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
if (preg_match('#^a\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
// route4
if ($pathinfo === '/route4') {
return array('_route' => 'route4');
@ -226,7 +226,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
if (preg_match('#^c\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
// route5
if ($pathinfo === '/route5') {
return array('_route' => 'route5');
@ -239,7 +239,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
return array('_route' => 'route6');
}
if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#si', $host, $hostMatches)) {
if (0 === strpos($pathinfo, '/route1')) {
// route11
if ($pathinfo === '/route11') {
@ -265,7 +265,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
if (preg_match('#^c\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
// route15
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ());

View File

@ -209,7 +209,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
$host = $this->context->getHost();
if (preg_match('#^a\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
// route1
if ($pathinfo === '/route1') {
return array('_route' => 'route1');
@ -222,7 +222,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
if (preg_match('#^b\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^b\\.example\\.com$#si', $host, $hostMatches)) {
// route3
if ($pathinfo === '/c2/route3') {
return array('_route' => 'route3');
@ -230,7 +230,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
if (preg_match('#^a\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
// route4
if ($pathinfo === '/route4') {
return array('_route' => 'route4');
@ -238,7 +238,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
if (preg_match('#^c\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
// route5
if ($pathinfo === '/route5') {
return array('_route' => 'route5');
@ -251,7 +251,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
return array('_route' => 'route6');
}
if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#si', $host, $hostMatches)) {
if (0 === strpos($pathinfo, '/route1')) {
// route11
if ($pathinfo === '/route11') {
@ -277,7 +277,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
if (preg_match('#^c\\.example\\.com$#s', $host, $hostMatches)) {
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
// route15
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ());

View File

@ -449,6 +449,33 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
$this->assertNull($generator->generate('test', array('foo' => 'baz'), false));
}
public function testHostIsCaseInsensitive()
{
$routes = $this->getRoutes('test', new Route('/', array(), array('locale' => 'en|de|fr'), array(), '{locale}.FooBar.com'));
$generator = $this->getGenerator($routes);
$this->assertSame('//EN.FooBar.com/app.php/', $generator->generate('test', array('locale' => 'EN'), UrlGeneratorInterface::NETWORK_PATH));
}
public function testLegacyGenerateNetworkPath()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$routes = $this->getRoutes('test', new Route('/{name}', array(), array('_scheme' => 'http'), array(), '{locale}.example.com'));
$this->assertSame('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test',
array('name' => 'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::NETWORK_PATH), 'network path with different host'
);
$this->assertSame('//fr.example.com/app.php/Fabien?query=string', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test',
array('name' => 'Fabien', 'locale' => 'fr', 'query' => 'string'), UrlGeneratorInterface::NETWORK_PATH), 'network path although host same as context'
);
$this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test',
array('name' => 'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::NETWORK_PATH), 'absolute URL because scheme requirement does not match context'
);
$this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test',
array('name' => 'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::ABSOLUTE_URL), 'absolute URL with same scheme because it is requested'
);
}
public function testGenerateNetworkPath()
{
$routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com', array('http')));

View File

@ -395,4 +395,25 @@ class UrlMatcherTest extends \PHPUnit_Framework_TestCase
$matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'example.com'));
$matcher->match('/foo/bar');
}
/**
* @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
*/
public function testPathIsCaseSensitive()
{
$coll = new RouteCollection();
$coll->add('foo', new Route('/locale', array(), array('locale' => 'EN|FR|DE')));
$matcher = new UrlMatcher($coll, new RequestContext());
$matcher->match('/en');
}
public function testHostIsCaseInsensitive()
{
$coll = new RouteCollection();
$coll->add('foo', new Route('/', array(), array('locale' => 'EN|FR|DE'), array(), '{locale}.example.com'));
$matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com'));
$this->assertEquals(array('_route' => 'foo', 'locale' => 'en'), $matcher->match('/'));
}
}

View File

@ -208,7 +208,7 @@ class RouteCompilerTest extends \PHPUnit_Framework_TestCase
'/hello', '#^/hello$#s', array(), array(), array(
array('text', '/hello'),
),
'#^www\.example\.com$#s', array(), array(
'#^www\.example\.com$#si', array(), array(
array('text', 'www.example.com'),
),
),
@ -219,7 +219,7 @@ class RouteCompilerTest extends \PHPUnit_Framework_TestCase
array('variable', '/', '[^/]++', 'name'),
array('text', '/hello'),
),
'#^www\.example\.(?P<tld>[^\.]++)$#s', array('tld'), array(
'#^www\.example\.(?P<tld>[^\.]++)$#si', array('tld'), array(
array('variable', '.', '[^\.]++', 'tld'),
array('text', 'www.example'),
),
@ -230,7 +230,7 @@ class RouteCompilerTest extends \PHPUnit_Framework_TestCase
'/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
array('text', '/hello'),
),
'#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#s', array('locale', 'tld'), array(
'#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#si', array('locale', 'tld'), array(
array('variable', '.', '[^\.]++', 'tld'),
array('text', '.example'),
array('variable', '', '[^\.]++', 'locale'),
@ -242,7 +242,7 @@ class RouteCompilerTest extends \PHPUnit_Framework_TestCase
'/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
array('text', '/hello'),
),
'#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#s', array('locale', 'tld'), array(
'#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#si', array('locale', 'tld'), array(
array('variable', '.', '[^\.]++', 'tld'),
array('text', '.example'),
array('variable', '', '[^\.]++', 'locale'),

View File

@ -226,7 +226,7 @@ class RouteTest extends \PHPUnit_Framework_TestCase
*/
public function testSerializedRepresentationKeepsWorking()
{
$serialized = 'C:31:"Symfony\Component\Routing\Route":933:{a:8:{s:4:"path";s:13:"/prefix/{foo}";s:4:"host";s:20:"{locale}.example.net";s:8:"defaults";a:1:{s:3:"foo";s:7:"default";}s:12:"requirements";a:1:{s:3:"foo";s:3:"\d+";}s:7:"options";a:1:{s:14:"compiler_class";s:39:"Symfony\Component\Routing\RouteCompiler";}s:7:"schemes";a:0:{}s:7:"methods";a:0:{}s:8:"compiled";C:39:"Symfony\Component\Routing\CompiledRoute":568:{a:8:{s:4:"vars";a:2:{i:0;s:6:"locale";i:1;s:3:"foo";}s:11:"path_prefix";s:7:"/prefix";s:10:"path_regex";s:30:"#^/prefix(?:/(?P<foo>\d+))?$#s";s:11:"path_tokens";a:2:{i:0;a:4:{i:0;s:8:"variable";i:1;s:1:"/";i:2;s:3:"\d+";i:3;s:3:"foo";}i:1;a:2:{i:0;s:4:"text";i:1;s:7:"/prefix";}}s:9:"path_vars";a:1:{i:0;s:3:"foo";}s:10:"host_regex";s:38:"#^(?P<locale>[^\.]++)\.example\.net$#s";s:11:"host_tokens";a:2:{i:0;a:2:{i:0;s:4:"text";i:1;s:12:".example.net";}i:1;a:4:{i:0;s:8:"variable";i:1;s:0:"";i:2;s:7:"[^\.]++";i:3;s:6:"locale";}}s:9:"host_vars";a:1:{i:0;s:6:"locale";}}}}}';
$serialized = 'C:31:"Symfony\Component\Routing\Route":934:{a:8:{s:4:"path";s:13:"/prefix/{foo}";s:4:"host";s:20:"{locale}.example.net";s:8:"defaults";a:1:{s:3:"foo";s:7:"default";}s:12:"requirements";a:1:{s:3:"foo";s:3:"\d+";}s:7:"options";a:1:{s:14:"compiler_class";s:39:"Symfony\Component\Routing\RouteCompiler";}s:7:"schemes";a:0:{}s:7:"methods";a:0:{}s:8:"compiled";C:39:"Symfony\Component\Routing\CompiledRoute":569:{a:8:{s:4:"vars";a:2:{i:0;s:6:"locale";i:1;s:3:"foo";}s:11:"path_prefix";s:7:"/prefix";s:10:"path_regex";s:30:"#^/prefix(?:/(?P<foo>\d+))?$#s";s:11:"path_tokens";a:2:{i:0;a:4:{i:0;s:8:"variable";i:1;s:1:"/";i:2;s:3:"\d+";i:3;s:3:"foo";}i:1;a:2:{i:0;s:4:"text";i:1;s:7:"/prefix";}}s:9:"path_vars";a:1:{i:0;s:3:"foo";}s:10:"host_regex";s:39:"#^(?P<locale>[^\.]++)\.example\.net$#si";s:11:"host_tokens";a:2:{i:0;a:2:{i:0;s:4:"text";i:1;s:12:".example.net";}i:1;a:4:{i:0;s:8:"variable";i:1;s:0:"";i:2;s:7:"[^\.]++";i:3;s:6:"locale";}}s:9:"host_vars";a:1:{i:0;s:6:"locale";}}}}}';
$unserialized = unserialize($serialized);
$route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+'));

View File

@ -60,7 +60,7 @@ class SecurityContext implements SecurityContextInterface
}
if ($oldSignature) {
// renamed for clearity
// renamed for clarity
$authenticationManager = $tokenStorage;
$accessDecisionManager = $authorizationChecker;
$tokenStorage = new TokenStorage();

View File

@ -1,11 +1,26 @@
CHANGELOG
=========
2.7.0
-----
* added support for serialization and deserialization groups including
annotations, XML and YAML mapping.
* added `AbstractNormalizer` to factorise code and ease normalizers development
* added circular references handling for `PropertyNormalizer`
* added support for a context key called `object_to_populate` in `AbstractNormalizer`
to reuse existing objects in the deserialization process
* added `NameConverterInterface` and `CamelCaseToSnakeCaseNameConverter`
* [DEPRECATION] `GetSetMethodNormalizer::setCamelizedAttributes()` and
`PropertyNormalizer::setCamelizedAttributes()` are replaced by
`CamelCaseToSnakeCaseNameConverter`
2.6.0
-----
* added a new serializer: `PropertyNormalizer`. Like `GetSetMethodNormalizer`,
this normalizer will map an object's properties to an array.
* added circular references handling for `GetSetMethodNormalizer`
2.5.0
-----

View File

@ -374,11 +374,9 @@ class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, Dec
} elseif ($key === '#') {
$append = $this->selectNodeType($parentNode, $data);
} elseif (is_array($data) && false === is_numeric($key)) {
/**
* Is this array fully numeric keys?
*/
// Is this array fully numeric keys?
if (ctype_digit(implode('', array_keys($data)))) {
/**
/*
* Create nodes to append to $parentNode based on the $key of this array
* Produces <xml><item>0</item><item>1</item></xml>
* From array("item" => array(0,1));.

View File

@ -86,12 +86,12 @@ class ClassMetadataFactory
$reflClass = $metadata->getReflectionClass();
// Include constraints from the parent class
// Include groups from the parent class
if ($parent = $reflClass->getParentClass()) {
$metadata->mergeAttributesGroups($this->getMetadataFor($parent->name));
}
// Include constraints from all implemented interfaces
// Include groups from all implemented interfaces
foreach ($reflClass->getInterfaces() as $interface) {
$metadata->mergeAttributesGroups($this->getMetadataFor($interface->name));
}

View File

@ -0,0 +1,82 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Serializer\NameConverter;
/**
* CamelCase to Underscore name converter.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class CamelCaseToSnakeCaseNameConverter implements NameConverterInterface
{
/**
* @var array|null
*/
private $attributes;
/**
* @var bool
*/
private $lowerCamelCase;
/**
* @param null|array $attributes The list of attributes to rename or null for all attributes.
* @param bool $lowerCamelCase Use lowerCamelCase style.
*/
public function __construct(array $attributes = null, $lowerCamelCase = true)
{
$this->attributes = $attributes;
$this->lowerCamelCase = $lowerCamelCase;
}
/**
* {@inheritdoc}
*/
public function normalize($propertyName)
{
if (null === $this->attributes || in_array($propertyName, $this->attributes)) {
$snakeCasedName = '';
$len = strlen($propertyName);
for ($i = 0; $i < $len; $i++) {
if (ctype_upper($propertyName[$i])) {
$snakeCasedName .= '_'.strtolower($propertyName[$i]);
} else {
$snakeCasedName .= strtolower($propertyName[$i]);
}
}
return $snakeCasedName;
}
return $propertyName;
}
/**
* {@inheritdoc}
*/
public function denormalize($propertyName)
{
$camelCasedName = preg_replace_callback('/(^|_|\.)+(.)/', function ($match) {
return ('.' === $match[1] ? '_' : '').strtoupper($match[2]);
}, $propertyName);
if ($this->lowerCamelCase) {
$camelCasedName = lcfirst($camelCasedName);
}
if (null === $this->attributes || in_array($camelCasedName, $this->attributes)) {
return $this->lowerCamelCase ? lcfirst($camelCasedName) : $camelCasedName;
}
return $propertyName;
}
}

View File

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Serializer\NameConverter;
/**
* Defines the interface for property name converters.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface NameConverterInterface
{
/**
* Converts a property name to its normalized value.
*
* @param string $propertyName
* @return string
*/
public function normalize($propertyName);
/**
* Converts a property name to its denormalized value.
*
* @param string $propertyName
* @return string
*/
public function denormalize($propertyName);
}

View File

@ -13,8 +13,11 @@ namespace Symfony\Component\Serializer\Normalizer;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
/**
* Normalizer implementation.
@ -26,6 +29,7 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
protected $circularReferenceLimit = 1;
protected $circularReferenceHandler;
protected $classMetadataFactory;
protected $nameConverter;
protected $callbacks = array();
protected $ignoredAttributes = array();
protected $camelizedAttributes = array();
@ -33,11 +37,13 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
/**
* Sets the {@link ClassMetadataFactory} to use.
*
* @param ClassMetadataFactory $classMetadataFactory
* @param ClassMetadataFactory|null $classMetadataFactory
* @param NameConverterInterface|null $nameConverter
*/
public function __construct(ClassMetadataFactory $classMetadataFactory = null)
public function __construct(ClassMetadataFactory $classMetadataFactory = null, NameConverterInterface $nameConverter = null)
{
$this->classMetadataFactory = $classMetadataFactory;
$this->nameConverter = $nameConverter;
}
/**
@ -115,13 +121,30 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
/**
* Set attributes to be camelized on denormalize.
*
* @deprecated Deprecated since version 2.7, to be removed in 3.0. Use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter instead.
*
* @param array $camelizedAttributes
*
* @return self
*
* @throws LogicException
*/
public function setCamelizedAttributes(array $camelizedAttributes)
{
$this->camelizedAttributes = $camelizedAttributes;
trigger_error(sprintf('%s is deprecated since version 2.7 and will be removed in 3.0. Use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter instead.', __METHOD__), E_USER_DEPRECATED);
if ($this->nameConverter && !$this->nameConverter instanceof CamelCaseToSnakeCaseNameConverter) {
throw new LogicException(sprintf('%s cannot be called if a custom Name Converter is defined.', __METHOD__));
}
$attributes = array();
foreach ($camelizedAttributes as $camelizedAttribute) {
$attributes[] = lcfirst(preg_replace_callback('/(^|_|\.)+(.)/', function ($match) {
return ('.' === $match[1] ? '_' : '').strtoupper($match[2]);
}, $camelizedAttribute));
}
$this->nameConverter = new CamelCaseToSnakeCaseNameConverter($attributes);
return $this;
}
@ -179,18 +202,17 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
/**
* Format an attribute name, for example to convert a snake_case name to camelCase.
*
* @deprecated Deprecated since version 2.7, to be removed in 3.0. Use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter instead.
*
* @param string $attributeName
*
* @return string
*/
protected function formatAttribute($attributeName)
{
if (in_array($attributeName, $this->camelizedAttributes)) {
return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) {
return ('.' === $match[1] ? '_' : '').strtoupper($match[2]);
}, $attributeName);
}
trigger_error(sprintf('%s is deprecated since version 2.7 and will be removed in 3.0. Use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter instead.', __METHOD__), E_USER_DEPRECATED);
return $attributeName;
return $this->nameConverter ? $this->nameConverter->normalize($attributeName) : $attributeName;
}
/**
@ -273,14 +295,15 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
$params = array();
foreach ($constructorParameters as $constructorParameter) {
$paramName = lcfirst($this->formatAttribute($constructorParameter->name));
$paramName = $constructorParameter->name;
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName) : $paramName;
$allowed = $allowedAttributes === false || in_array($paramName, $allowedAttributes);
$ignored = in_array($paramName, $this->ignoredAttributes);
if ($allowed && !$ignored && isset($data[$paramName])) {
$params[] = $data[$paramName];
if ($allowed && !$ignored && isset($data[$key])) {
$params[] = $data[$key];
// don't run set for a parameter passed to the constructor
unset($data[$paramName]);
unset($data[$key]);
} elseif ($constructorParameter->isOptional()) {
$params[] = $constructorParameter->getDefaultValue();
} else {

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Serializer\Normalizer;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\RuntimeException;
/**
@ -40,6 +41,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
/**
* {@inheritdoc}
*
* @throws LogicException
* @throws CircularReferenceException
*/
public function normalize($object, $format = null, array $context = array())
@ -71,12 +73,16 @@ class GetSetMethodNormalizer extends AbstractNormalizer
}
if (null !== $attributeValue && !is_scalar($attributeValue)) {
if (!$this->serializer instanceof NormalizerInterface) {
throw new \LogicException(sprintf('Cannot normalize attribute "%s" because injected serializer is not a normalizer', $attributeName));
throw new LogicException(sprintf('Cannot normalize attribute "%s" because injected serializer is not a normalizer', $attributeName));
}
$attributeValue = $this->serializer->normalize($attributeValue, $format, $context);
}
if ($this->nameConverter) {
$attributeName = $this->nameConverter->normalize($attributeName);
}
$attributes[$attributeName] = $attributeValue;
}
}
@ -102,7 +108,11 @@ class GetSetMethodNormalizer extends AbstractNormalizer
$ignored = in_array($attribute, $this->ignoredAttributes);
if ($allowed && !$ignored) {
$setter = 'set'.$this->formatAttribute($attribute);
if ($this->nameConverter) {
$attribute = $this->nameConverter->denormalize($attribute);
}
$setter = 'set'.ucfirst($attribute);
if (method_exists($object, $setter)) {
$object->$setter($value);

View File

@ -71,7 +71,12 @@ class PropertyNormalizer extends AbstractNormalizer
$attributeValue = $this->serializer->normalize($attributeValue, $format, $context);
}
$attributes[$property->name] = $attributeValue;
$propertyName = $property->name;
if ($this->nameConverter) {
$propertyName = $this->nameConverter->normalize($propertyName);
}
$attributes[$propertyName] = $attributeValue;
}
return $attributes;
@ -91,7 +96,9 @@ class PropertyNormalizer extends AbstractNormalizer
$object = $this->instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes);
foreach ($data as $propertyName => $value) {
$propertyName = lcfirst($this->formatAttribute($propertyName));
if ($this->nameConverter) {
$propertyName = $this->nameConverter->denormalize($propertyName);
}
$allowed = $allowedAttributes === false || in_array($propertyName, $allowedAttributes);
$ignored = in_array($propertyName, $this->ignoredAttributes);

View File

@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Serializer\Tests\NameConverter;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class CamelCaseToSnakeCaseNameConverterTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider attributeProvider
*/
public function testNormalize($underscored, $lowerCamelCased)
{
$nameConverter = new CamelCaseToSnakeCaseNameConverter();
$this->assertEquals($nameConverter->normalize($lowerCamelCased), $underscored);
}
/**
* @dataProvider attributeProvider
*/
public function testDenormalize($underscored, $lowerCamelCased)
{
$nameConverter = new CamelCaseToSnakeCaseNameConverter();
$this->assertEquals($nameConverter->denormalize($underscored), $lowerCamelCased);
}
public function attributeProvider()
{
return array(
array('coop_tilleuls', 'coopTilleuls'),
array('_kevin_dunglas', '_kevinDunglas'),
array('this_is_a_test', 'thisIsATest'),
);
}
}

View File

@ -22,8 +22,6 @@ use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy;
require_once __DIR__.'/../../Annotation/Groups.php';
class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
{
/**
@ -95,13 +93,16 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('bar', $obj->getBar());
}
public function testDenormalizeOnCamelCaseFormat()
public function testLegacyDenormalizeOnCamelCaseFormat()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$this->normalizer->setCamelizedAttributes(array('camel_case'));
$obj = $this->normalizer->denormalize(
array('camel_case' => 'camelCase'),
__NAMESPACE__.'\GetSetDummy'
);
$this->assertEquals('camelCase', $obj->getCamelCase());
}
@ -110,27 +111,50 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(new GetSetDummy(), $this->normalizer->denormalize(null, __NAMESPACE__.'\GetSetDummy'));
}
/**
* @dataProvider attributeProvider
*/
public function testFormatAttribute($attribute, $camelizedAttributes, $result)
public function testLegacyCamelizedAttributesNormalize()
{
$r = new \ReflectionObject($this->normalizer);
$m = $r->getMethod('formatAttribute');
$m->setAccessible(true);
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$this->normalizer->setCamelizedAttributes($camelizedAttributes);
$this->assertEquals($m->invoke($this->normalizer, $attribute, $camelizedAttributes), $result);
$obj = new GetCamelizedDummy('dunglas.fr');
$obj->setFooBar('les-tilleuls.coop');
$obj->setBar_foo('lostinthesupermarket.fr');
$this->normalizer->setCamelizedAttributes(array('kevin_dunglas'));
$this->assertEquals($this->normalizer->normalize($obj), array(
'kevin_dunglas' => 'dunglas.fr',
'fooBar' => 'les-tilleuls.coop',
'bar_foo' => 'lostinthesupermarket.fr',
));
$this->normalizer->setCamelizedAttributes(array('foo_bar'));
$this->assertEquals($this->normalizer->normalize($obj), array(
'kevinDunglas' => 'dunglas.fr',
'foo_bar' => 'les-tilleuls.coop',
'bar_foo' => 'lostinthesupermarket.fr',
));
}
public function attributeProvider()
public function testLegacyCamelizedAttributesDenormalize()
{
return array(
array('attribute_test', array('attribute_test'),'AttributeTest'),
array('attribute_test', array('any'),'attribute_test'),
array('attribute', array('attribute'),'Attribute'),
array('attribute', array(), 'attribute'),
);
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$obj = new GetCamelizedDummy('dunglas.fr');
$obj->setFooBar('les-tilleuls.coop');
$obj->setBar_foo('lostinthesupermarket.fr');
$this->normalizer->setCamelizedAttributes(array('kevin_dunglas'));
$this->assertEquals($this->normalizer->denormalize(array(
'kevin_dunglas' => 'dunglas.fr',
'fooBar' => 'les-tilleuls.coop',
'bar_foo' => 'lostinthesupermarket.fr',
), __NAMESPACE__.'\GetCamelizedDummy'), $obj);
$this->normalizer->setCamelizedAttributes(array('foo_bar'));
$this->assertEquals($this->normalizer->denormalize(array(
'kevinDunglas' => 'dunglas.fr',
'foo_bar' => 'les-tilleuls.coop',
'bar_foo' => 'lostinthesupermarket.fr',
), __NAMESPACE__.'\GetCamelizedDummy'), $obj);
}
public function testConstructorDenormalize()
@ -328,7 +352,7 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
}
/**
* @expectedException \LogicException
* @expectedException \Symfony\Component\Serializer\Exception\LogicException
* @expectedExceptionMessage Cannot normalize attribute "object" because injected serializer is not a normalizer
*/
public function testUnableToNormalizeObjectAttribute()
@ -544,3 +568,40 @@ class GetConstructorOptionalArgsDummy
throw new \RuntimeException("Dummy::otherMethod() should not be called");
}
}
class GetCamelizedDummy
{
private $kevinDunglas;
private $fooBar;
private $bar_foo;
public function __construct($kevinDunglas = null)
{
$this->kevinDunglas = $kevinDunglas;
}
public function getKevinDunglas()
{
return $this->kevinDunglas;
}
public function setFooBar($fooBar)
{
$this->fooBar = $fooBar;
}
public function getFooBar()
{
return $this->fooBar;
}
public function setBar_foo($bar_foo)
{
$this->bar_foo = $bar_foo;
}
public function getBar_foo()
{
return $this->bar_foo;
}
}

View File

@ -21,8 +21,6 @@ use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy;
use Symfony\Component\Serializer\Tests\Fixtures\PropertyCircularReferenceDummy;
use Symfony\Component\Serializer\Tests\Fixtures\PropertySiblingHolder;
require_once __DIR__.'/../../Annotation/Groups.php';
class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase
{
/**
@ -64,8 +62,10 @@ class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('bar', $obj->getBar());
}
public function testDenormalizeOnCamelCaseFormat()
public function testLegacyDenormalizeOnCamelCaseFormat()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$this->normalizer->setCamelizedAttributes(array('camel_case'));
$obj = $this->normalizer->denormalize(
array('camel_case' => 'value'),
@ -74,27 +74,50 @@ class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('value', $obj->getCamelCase());
}
/**
* @dataProvider attributeProvider
*/
public function testFormatAttribute($attribute, $camelizedAttributes, $result)
public function testLegacyCamelizedAttributesNormalize()
{
$r = new \ReflectionObject($this->normalizer);
$m = $r->getMethod('formatAttribute');
$m->setAccessible(true);
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$this->normalizer->setCamelizedAttributes($camelizedAttributes);
$this->assertEquals($m->invoke($this->normalizer, $attribute, $camelizedAttributes), $result);
$obj = new PropertyCamelizedDummy('dunglas.fr');
$obj->fooBar = 'les-tilleuls.coop';
$obj->bar_foo = 'lostinthesupermarket.fr';
$this->normalizer->setCamelizedAttributes(array('kevin_dunglas'));
$this->assertEquals($this->normalizer->normalize($obj), array(
'kevin_dunglas' => 'dunglas.fr',
'fooBar' => 'les-tilleuls.coop',
'bar_foo' => 'lostinthesupermarket.fr',
));
$this->normalizer->setCamelizedAttributes(array('foo_bar'));
$this->assertEquals($this->normalizer->normalize($obj), array(
'kevinDunglas' => 'dunglas.fr',
'foo_bar' => 'les-tilleuls.coop',
'bar_foo' => 'lostinthesupermarket.fr',
));
}
public function attributeProvider()
public function testLegacyCamelizedAttributesDenormalize()
{
return array(
array('attribute_test', array('attribute_test'),'AttributeTest'),
array('attribute_test', array('any'),'attribute_test'),
array('attribute', array('attribute'),'Attribute'),
array('attribute', array(), 'attribute'),
);
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$obj = new PropertyCamelizedDummy('dunglas.fr');
$obj->fooBar = 'les-tilleuls.coop';
$obj->bar_foo = 'lostinthesupermarket.fr';
$this->normalizer->setCamelizedAttributes(array('kevin_dunglas'));
$this->assertEquals($this->normalizer->denormalize(array(
'kevin_dunglas' => 'dunglas.fr',
'fooBar' => 'les-tilleuls.coop',
'bar_foo' => 'lostinthesupermarket.fr',
), __NAMESPACE__.'\PropertyCamelizedDummy'), $obj);
$this->normalizer->setCamelizedAttributes(array('foo_bar'));
$this->assertEquals($this->normalizer->denormalize(array(
'kevinDunglas' => 'dunglas.fr',
'foo_bar' => 'les-tilleuls.coop',
'bar_foo' => 'lostinthesupermarket.fr',
), __NAMESPACE__.'\PropertyCamelizedDummy'), $obj);
}
public function testConstructorDenormalize()
@ -360,3 +383,15 @@ class PropertyConstructorDummy
return $this->bar;
}
}
class PropertyCamelizedDummy
{
private $kevinDunglas;
public $fooBar;
public $bar_foo;
public function __construct($kevinDunglas = null)
{
$this->kevinDunglas = $kevinDunglas;
}
}

View File

@ -0,0 +1,14 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
$loader = require __DIR__.'/../vendor/autoload.php';
Doctrine\Common\Annotations\AnnotationRegistry::registerLoader(function ($class) { return class_exists($class); });

View File

@ -4,7 +4,7 @@
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
bootstrap="Tests/bootstrap.php"
>
<php>
<!-- Disable E_USER_DEPRECATED until 3.0 -->

View File

@ -28,72 +28,52 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class CardSchemeValidator extends ConstraintValidator
{
protected $schemes = array(
/**
* American Express card numbers start with 34 or 37 and have 15 digits.
*/
// American Express card numbers start with 34 or 37 and have 15 digits.
'AMEX' => array(
'/^3[47][0-9]{13}$/',
),
/**
* China UnionPay cards start with 62 and have between 16 and 19 digits.
* Please note that these cards do not follow Luhn Algorithm as a checksum.
*/
// China UnionPay cards start with 62 and have between 16 and 19 digits.
// Please note that these cards do not follow Luhn Algorithm as a checksum.
'CHINA_UNIONPAY' => array(
'/^62[0-9]{14,17}$/',
),
/**
* Diners Club card numbers begin with 300 through 305, 36 or 38. All have 14 digits.
* There are Diners Club cards that begin with 5 and have 16 digits.
* These are a joint venture between Diners Club and MasterCard, and should be processed like a MasterCard.
*/
// Diners Club card numbers begin with 300 through 305, 36 or 38. All have 14 digits.
// There are Diners Club cards that begin with 5 and have 16 digits.
// These are a joint venture between Diners Club and MasterCard, and should be processed like a MasterCard.
'DINERS' => array(
'/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/',
),
/**
* Discover card numbers begin with 6011, 622126 through 622925, 644 through 649 or 65.
* All have 16 digits.
*/
// Discover card numbers begin with 6011, 622126 through 622925, 644 through 649 or 65.
// All have 16 digits.
'DISCOVER' => array(
'/^6011[0-9]{12}$/',
'/^64[4-9][0-9]{13}$/',
'/^65[0-9]{14}$/',
'/^622(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|91[0-9]|92[0-5])[0-9]{10}$/',
),
/**
* InstaPayment cards begin with 637 through 639 and have 16 digits.
*/
// InstaPayment cards begin with 637 through 639 and have 16 digits.
'INSTAPAYMENT' => array(
'/^63[7-9][0-9]{13}$/',
),
/**
* JCB cards beginning with 2131 or 1800 have 15 digits.
* JCB cards beginning with 35 have 16 digits.
*/
// JCB cards beginning with 2131 or 1800 have 15 digits.
// JCB cards beginning with 35 have 16 digits.
'JCB' => array(
'/^(?:2131|1800|35[0-9]{3})[0-9]{11}$/',
),
/**
* Laser cards begin with either 6304, 6706, 6709 or 6771 and have between 16 and 19 digits.
*/
// Laser cards begin with either 6304, 6706, 6709 or 6771 and have between 16 and 19 digits.
'LASER' => array(
'/^(6304|670[69]|6771)[0-9]{12,15}$/',
),
/**
* Maestro cards begin with either 5018, 5020, 5038, 5893, 6304, 6759, 6761, 6762, 6763 or 0604
* They have between 12 and 19 digits.
*/
// Maestro cards begin with either 5018, 5020, 5038, 5893, 6304, 6759, 6761, 6762, 6763 or 0604
// They have between 12 and 19 digits.
'MAESTRO' => array(
'/^(5018|5020|5038|6304|6759|6761|676[23]|0604)[0-9]{8,15}$/',
),
/**
* All MasterCard numbers start with the numbers 51 through 55. All have 16 digits.
*/
// All MasterCard numbers start with the numbers 51 through 55. All have 16 digits.
'MASTERCARD' => array(
'/^5[1-5][0-9]{14}$/',
),
/**
* All Visa card numbers start with a 4. New cards have 16 digits. Old cards have 13.
*/
// All Visa card numbers start with a 4. New cards have 16 digits. Old cards have 13.
'VISA' => array(
'/^4([0-9]{12}|[0-9]{15})$/',
),

View File

@ -35,6 +35,7 @@ class Length extends Constraint
public $maxMessage = 'This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.';
public $minMessage = 'This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.';
public $exactMessage = 'This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.';
public $charsetMessage = 'This value does not match the expected {{ charset }} charset.';
public $max;
public $min;
public $charset = 'UTF-8';

View File

@ -39,13 +39,39 @@ class LengthValidator extends ConstraintValidator
}
$stringValue = (string) $value;
$invalidCharset = false;
if (function_exists('grapheme_strlen') && 'UTF-8' === $constraint->charset) {
$length = grapheme_strlen($stringValue);
if ('UTF8' === $charset = strtoupper($constraint->charset)) {
$charset = 'UTF-8';
}
if (function_exists('iconv_strlen')) {
$length = @iconv_strlen($stringValue, $constraint->charset);
$invalidCharset = false === $length;
} elseif (function_exists('mb_strlen')) {
$length = mb_strlen($stringValue, $constraint->charset);
} else {
if (mb_check_encoding($stringValue, $constraint->charset)) {
$length = mb_strlen($stringValue, $constraint->charset);
} else {
$invalidCharset = true;
}
} elseif ('UTF-8' !== $charset) {
$length = strlen($stringValue);
} elseif (!preg_match('//u', $stringValue)) {
$invalidCharset = true;
} elseif (function_exists('utf8_decode')) {
$length = strlen(utf8_decode($stringValue));
} else {
preg_replace('/./u', '', $stringValue, -1, $length);
}
if ($invalidCharset) {
$this->buildViolation($constraint->charsetMessage)
->setParameter('{{ value }}', $this->formatValue($stringValue))
->setParameter('{{ charset }}', $constraint->charset)
->setInvalidValue($value)
->addViolation();
return;
}
if (null !== $constraint->max && $length > $constraint->max) {

View File

@ -23,7 +23,7 @@ abstract class ElementMetadata extends GenericMetadata
{
public function __construct()
{
if (__CLASS__ === get_class($this) || !in_array(get_parent_class($this), array('Symfony\Component\Validator\Mapping\MemberMetadata', 'Symfony\Component\Validator\Mapping\ClassMetadata'))) {
if (!$this instanceof MemberMetadata && !$this instanceof ClassMetadata) {
trigger_error('The '.__CLASS__.' class is deprecated since version 2.5 and will be removed in 3.0. Use the Symfony\Component\Validator\Mapping\GenericMetadata class instead.', E_USER_DEPRECATED);
}
}

View File

@ -1,283 +1,283 @@
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>This value should be false.</source>
<target>Стойността трябва да бъде лъжа (false).</target>
</trans-unit>
<trans-unit id="2">
<source>This value should be true.</source>
<target>Стойността трябва да бъде истина (true).</target>
</trans-unit>
<trans-unit id="3">
<source>This value should be of type {{ type }}.</source>
<target>Стойността трябва да бъде от тип {{ type }}.</target>
</trans-unit>
<trans-unit id="4">
<source>This value should be blank.</source>
<target>Стойността трябва да бъде празна.</target>
</trans-unit>
<trans-unit id="5">
<source>The value you selected is not a valid choice.</source>
<target>Избраната стойност е невалидна.</target>
</trans-unit>
<trans-unit id="6">
<source>You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.</source>
<target>Трябва да изберете поне {{ limit }} опция.|Трябва да изберете поне {{ limit }} опции.</target>
</trans-unit>
<trans-unit id="7">
<source>You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.</source>
<target>Трябва да изберете най-много {{ limit }} опция.|Трябва да изберете най-много {{ limit }} опции.</target>
</trans-unit>
<trans-unit id="8">
<source>One or more of the given values is invalid.</source>
<target>Една или повече от зададените стойности е невалидна.</target>
</trans-unit>
<trans-unit id="9">
<source>This field was not expected.</source>
<target>Това поле не се е очаквало.</target>
</trans-unit>
<trans-unit id="10">
<source>This field is missing.</source>
<target>Това поле липсва.</target>
</trans-unit>
<trans-unit id="11">
<source>This value is not a valid date.</source>
<target>Стойността не е валидна дата (date).</target>
</trans-unit>
<trans-unit id="12">
<source>This value is not a valid datetime.</source>
<target>Стойността не е валидна дата (datetime).</target>
</trans-unit>
<trans-unit id="13">
<source>This value is not a valid email address.</source>
<target>Стойността не е валиден email адрес.</target>
</trans-unit>
<trans-unit id="14">
<source>The file could not be found.</source>
<target>Файлът не беше открит.</target>
</trans-unit>
<trans-unit id="15">
<source>The file is not readable.</source>
<target>Файлът не може да бъде прочетен.</target>
</trans-unit>
<trans-unit id="16">
<source>The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.</source>
<target>Файлът е твърде голям ({{ size }} {{ suffix }}). Максималният размер е {{ limit }} {{ suffix }}.</target>
</trans-unit>
<trans-unit id="17">
<source>The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.</source>
<target>Майм типа на файла е невалиден ({{ type }}). Разрешени майм типове са {{ types }}.</target>
</trans-unit>
<trans-unit id="18">
<source>This value should be {{ limit }} or less.</source>
<target>Стойността трябва да бъде {{ limit }} или по-малко.</target>
</trans-unit>
<trans-unit id="19">
<source>This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.</source>
<target>Стойността е твърде дълга. Трябва да съдържа най-много {{ limit }} символ.|Стойността е твърде дълга. Трябва да съдържа най-много {{ limit }} символа.</target>
</trans-unit>
<trans-unit id="20">
<source>This value should be {{ limit }} or more.</source>
<target>Стойността трябва да бъде {{ limit }} или повече.</target>
</trans-unit>
<trans-unit id="21">
<source>This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.</source>
<target>Стойността е твърде кратка. Трябва да съдържа поне {{ limit }} символ.|Стойността е твърде кратка. Трябва да съдържа поне {{ limit }} символа.</target>
</trans-unit>
<trans-unit id="22">
<source>This value should not be blank.</source>
<target>Стойността не трябва да бъде празна.</target>
</trans-unit>
<trans-unit id="23">
<source>This value should not be null.</source>
<target>Стойността не трябва да бъде null.</target>
</trans-unit>
<trans-unit id="24">
<source>This value should be null.</source>
<target>Стойността трябва да бъде null.</target>
</trans-unit>
<trans-unit id="25">
<source>This value is not valid.</source>
<target>Стойността не е валидна.</target>
</trans-unit>
<trans-unit id="26">
<source>This value is not a valid time.</source>
<target>Стойността не е валидно време (time).</target>
</trans-unit>
<trans-unit id="27">
<source>This value is not a valid URL.</source>
<target>Стойността не е валиден URL.</target>
</trans-unit>
<trans-unit id="31">
<source>The two values should be equal.</source>
<target>Двете стойности трябва да бъдат равни.</target>
</trans-unit>
<trans-unit id="32">
<source>The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.</source>
<target>Файлът е твърде голям. Разрешеният максимален размер е {{ limit }} {{ suffix }}.</target>
</trans-unit>
<trans-unit id="33">
<source>The file is too large.</source>
<target>Файлът е твърде голям.</target>
</trans-unit>
<trans-unit id="34">
<source>The file could not be uploaded.</source>
<target>Файлът не може да бъде качен.</target>
</trans-unit>
<trans-unit id="35">
<source>This value should be a valid number.</source>
<target>Стойността трябва да бъде валиден номер.</target>
</trans-unit>
<trans-unit id="36">
<source>This file is not a valid image.</source>
<target>Файлът не е валидно изображение.</target>
</trans-unit>
<trans-unit id="37">
<source>This is not a valid IP address.</source>
<target>Това не е валиден IP адрес.</target>
</trans-unit>
<trans-unit id="38">
<source>This value is not a valid language.</source>
<target>Стойността не е валиден език.</target>
</trans-unit>
<trans-unit id="39">
<source>This value is not a valid locale.</source>
<target>Стойността не е валидна локализация.</target>
</trans-unit>
<trans-unit id="40">
<source>This value is not a valid country.</source>
<target>Стойността не е валидна държава.</target>
</trans-unit>
<trans-unit id="41">
<source>This value is already used.</source>
<target>Стойността вече е в употреба.</target>
</trans-unit>
<trans-unit id="42">
<source>The size of the image could not be detected.</source>
<target>Размера на изображението не може да бъде определен.</target>
</trans-unit>
<trans-unit id="43">
<source>The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.</source>
<target>Изображението е твърде широко ({{ width }}px). Широчината трябва да бъде максимум {{ max_width }}px.</target>
</trans-unit>
<trans-unit id="44">
<source>The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.</source>
<target>Изображението е с твърде малка широчина ({{ width }}px). Широчината трябва да бъде минимум {{ min_width }}px.</target>
</trans-unit>
<trans-unit id="45">
<source>The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.</source>
<target>Изображението е с твърде голяма височина ({{ height }}px). Височината трябва да бъде максимум {{ max_height }}px.</target>
</trans-unit>
<trans-unit id="46">
<source>The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.</source>
<target>Изображението е с твърде малка височина ({{ height }}px). Височина трябва да бъде минимум {{ min_height }}px.</target>
</trans-unit>
<trans-unit id="47">
<source>This value should be the user's current password.</source>
<target>Стойността трябва да бъде текущата потребителска парола.</target>
</trans-unit>
<trans-unit id="48">
<source>This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.</source>
<target>Стойността трябва да бъде точно {{ limit }} символ.|Стойността трябва да бъде точно {{ limit }} символа.</target>
</trans-unit>
<trans-unit id="49">
<source>The file was only partially uploaded.</source>
<target>Файлът е качен частично.</target>
</trans-unit>
<trans-unit id="50">
<source>No file was uploaded.</source>
<target>Файлът не беше качен.</target>
</trans-unit>
<trans-unit id="51">
<source>No temporary folder was configured in php.ini.</source>
<target>Не е посочена директория за временни файлове в php.ini.</target>
</trans-unit>
<trans-unit id="52">
<source>Cannot write temporary file to disk.</source>
<target>Не може да запише временен файл на диска.</target>
</trans-unit>
<trans-unit id="53">
<source>A PHP extension caused the upload to fail.</source>
<target>PHP разширение предизвика прекъсване на качването.</target>
</trans-unit>
<trans-unit id="54">
<source>This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.</source>
<target>Колекцията трябва да съдържа поне {{ limit }} елемент.|Колекцията трябва да съдържа поне {{ limit }} елемента.</target>
</trans-unit>
<trans-unit id="55">
<source>This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.</source>
<target>Колекцията трябва да съдържа най-много {{ limit }} елемент.|Колекцията трябва да съдържа най-много {{ limit }} елемента.</target>
</trans-unit>
<trans-unit id="56">
<source>This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.</source>
<target>Колекцията трябва да съдържа точно {{ limit }} елемент.|Колекцията трябва да съдържа точно {{ limit }} елемента.</target>
</trans-unit>
<trans-unit id="57">
<source>Invalid card number.</source>
<target>Невалиден номер на картата.</target>
</trans-unit>
<trans-unit id="58">
<source>Unsupported card type or invalid card number.</source>
<target>Неподдържан тип карта или невалиден номер на картата.</target>
</trans-unit>
<trans-unit id="59">
<source>This is not a valid International Bank Account Number (IBAN).</source>
<target>Невалиден Международен номер на банкова сметка (IBAN).</target>
</trans-unit>
<trans-unit id="60">
<source>This value is not a valid ISBN-10.</source>
<target>Невалиден ISBN-10.</target>
</trans-unit>
<trans-unit id="61">
<source>This value is not a valid ISBN-13.</source>
<target>Невалиден ISBN-13.</target>
</trans-unit>
<trans-unit id="62">
<source>This value is neither a valid ISBN-10 nor a valid ISBN-13.</source>
<target>Невалидна стойност както за ISBN-10, така и за ISBN-13 .</target>
</trans-unit>
<trans-unit id="63">
<source>This value is not a valid ISSN.</source>
<target>Невалиден Международен стандартен сериен номер (ISSN).</target>
</trans-unit>
<trans-unit id="64">
<source>This value is not a valid currency.</source>
<target>Невалидна валута.</target>
</trans-unit>
<trans-unit id="65">
<source>This value should be equal to {{ compared_value }}.</source>
<target>Стойността трябва да бъде равна на {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="66">
<source>This value should be greater than {{ compared_value }}.</source>
<target>Стойността трябва да бъде по-голяма от {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="67">
<source>This value should be greater than or equal to {{ compared_value }}.</source>
<target>Стойността трябва да бъде по-голяма или равна на {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="68">
<source>This value should be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>Стойността трябва да бъде идентична с {{ compared_value_type }} {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="69">
<source>This value should be less than {{ compared_value }}.</source>
<target>Стойността трябва да бъде по-малка {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="70">
<source>This value should be less than or equal to {{ compared_value }}.</source>
<target>Стойността трябва да бъде по-малка или равна на {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="71">
<source>This value should not be equal to {{ compared_value }}.</source>
<target>Стойността не трябва да бъде равна на {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="72">
<source>This value should not be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>Стойността не трябва да бъде идентична с {{ compared_value_type }} {{ compared_value }}.</target>
</trans-unit>
</body>
</file>
</xliff>
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>This value should be false.</source>
<target>Стойността трябва да бъде лъжа (false).</target>
</trans-unit>
<trans-unit id="2">
<source>This value should be true.</source>
<target>Стойността трябва да бъде истина (true).</target>
</trans-unit>
<trans-unit id="3">
<source>This value should be of type {{ type }}.</source>
<target>Стойността трябва да бъде от тип {{ type }}.</target>
</trans-unit>
<trans-unit id="4">
<source>This value should be blank.</source>
<target>Стойността трябва да бъде празна.</target>
</trans-unit>
<trans-unit id="5">
<source>The value you selected is not a valid choice.</source>
<target>Избраната стойност е невалидна.</target>
</trans-unit>
<trans-unit id="6">
<source>You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.</source>
<target>Трябва да изберете поне {{ limit }} опция.|Трябва да изберете поне {{ limit }} опции.</target>
</trans-unit>
<trans-unit id="7">
<source>You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.</source>
<target>Трябва да изберете най-много {{ limit }} опция.|Трябва да изберете най-много {{ limit }} опции.</target>
</trans-unit>
<trans-unit id="8">
<source>One or more of the given values is invalid.</source>
<target>Една или повече от зададените стойности е невалидна.</target>
</trans-unit>
<trans-unit id="9">
<source>This field was not expected.</source>
<target>Това поле не се е очаквало.</target>
</trans-unit>
<trans-unit id="10">
<source>This field is missing.</source>
<target>Това поле липсва.</target>
</trans-unit>
<trans-unit id="11">
<source>This value is not a valid date.</source>
<target>Стойността не е валидна дата (date).</target>
</trans-unit>
<trans-unit id="12">
<source>This value is not a valid datetime.</source>
<target>Стойността не е валидна дата (datetime).</target>
</trans-unit>
<trans-unit id="13">
<source>This value is not a valid email address.</source>
<target>Стойността не е валиден email адрес.</target>
</trans-unit>
<trans-unit id="14">
<source>The file could not be found.</source>
<target>Файлът не беше открит.</target>
</trans-unit>
<trans-unit id="15">
<source>The file is not readable.</source>
<target>Файлът не може да бъде прочетен.</target>
</trans-unit>
<trans-unit id="16">
<source>The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.</source>
<target>Файлът е твърде голям ({{ size }} {{ suffix }}). Максималният размер е {{ limit }} {{ suffix }}.</target>
</trans-unit>
<trans-unit id="17">
<source>The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.</source>
<target>Майм типа на файла е невалиден ({{ type }}). Разрешени майм типове са {{ types }}.</target>
</trans-unit>
<trans-unit id="18">
<source>This value should be {{ limit }} or less.</source>
<target>Стойността трябва да бъде {{ limit }} или по-малко.</target>
</trans-unit>
<trans-unit id="19">
<source>This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.</source>
<target>Стойността е твърде дълга. Трябва да съдържа най-много {{ limit }} символ.|Стойността е твърде дълга. Трябва да съдържа най-много {{ limit }} символа.</target>
</trans-unit>
<trans-unit id="20">
<source>This value should be {{ limit }} or more.</source>
<target>Стойността трябва да бъде {{ limit }} или повече.</target>
</trans-unit>
<trans-unit id="21">
<source>This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.</source>
<target>Стойността е твърде кратка. Трябва да съдържа поне {{ limit }} символ.|Стойността е твърде кратка. Трябва да съдържа поне {{ limit }} символа.</target>
</trans-unit>
<trans-unit id="22">
<source>This value should not be blank.</source>
<target>Стойността не трябва да бъде празна.</target>
</trans-unit>
<trans-unit id="23">
<source>This value should not be null.</source>
<target>Стойността не трябва да бъде null.</target>
</trans-unit>
<trans-unit id="24">
<source>This value should be null.</source>
<target>Стойността трябва да бъде null.</target>
</trans-unit>
<trans-unit id="25">
<source>This value is not valid.</source>
<target>Стойността не е валидна.</target>
</trans-unit>
<trans-unit id="26">
<source>This value is not a valid time.</source>
<target>Стойността не е валидно време (time).</target>
</trans-unit>
<trans-unit id="27">
<source>This value is not a valid URL.</source>
<target>Стойността не е валиден URL.</target>
</trans-unit>
<trans-unit id="31">
<source>The two values should be equal.</source>
<target>Двете стойности трябва да бъдат равни.</target>
</trans-unit>
<trans-unit id="32">
<source>The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.</source>
<target>Файлът е твърде голям. Разрешеният максимален размер е {{ limit }} {{ suffix }}.</target>
</trans-unit>
<trans-unit id="33">
<source>The file is too large.</source>
<target>Файлът е твърде голям.</target>
</trans-unit>
<trans-unit id="34">
<source>The file could not be uploaded.</source>
<target>Файлът не може да бъде качен.</target>
</trans-unit>
<trans-unit id="35">
<source>This value should be a valid number.</source>
<target>Стойността трябва да бъде валиден номер.</target>
</trans-unit>
<trans-unit id="36">
<source>This file is not a valid image.</source>
<target>Файлът не е валидно изображение.</target>
</trans-unit>
<trans-unit id="37">
<source>This is not a valid IP address.</source>
<target>Това не е валиден IP адрес.</target>
</trans-unit>
<trans-unit id="38">
<source>This value is not a valid language.</source>
<target>Стойността не е валиден език.</target>
</trans-unit>
<trans-unit id="39">
<source>This value is not a valid locale.</source>
<target>Стойността не е валидна локализация.</target>
</trans-unit>
<trans-unit id="40">
<source>This value is not a valid country.</source>
<target>Стойността не е валидна държава.</target>
</trans-unit>
<trans-unit id="41">
<source>This value is already used.</source>
<target>Стойността вече е в употреба.</target>
</trans-unit>
<trans-unit id="42">
<source>The size of the image could not be detected.</source>
<target>Размера на изображението не може да бъде определен.</target>
</trans-unit>
<trans-unit id="43">
<source>The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.</source>
<target>Изображението е твърде широко ({{ width }}px). Широчината трябва да бъде максимум {{ max_width }}px.</target>
</trans-unit>
<trans-unit id="44">
<source>The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.</source>
<target>Изображението е с твърде малка широчина ({{ width }}px). Широчината трябва да бъде минимум {{ min_width }}px.</target>
</trans-unit>
<trans-unit id="45">
<source>The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.</source>
<target>Изображението е с твърде голяма височина ({{ height }}px). Височината трябва да бъде максимум {{ max_height }}px.</target>
</trans-unit>
<trans-unit id="46">
<source>The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.</source>
<target>Изображението е с твърде малка височина ({{ height }}px). Височина трябва да бъде минимум {{ min_height }}px.</target>
</trans-unit>
<trans-unit id="47">
<source>This value should be the user's current password.</source>
<target>Стойността трябва да бъде текущата потребителска парола.</target>
</trans-unit>
<trans-unit id="48">
<source>This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.</source>
<target>Стойността трябва да бъде точно {{ limit }} символ.|Стойността трябва да бъде точно {{ limit }} символа.</target>
</trans-unit>
<trans-unit id="49">
<source>The file was only partially uploaded.</source>
<target>Файлът е качен частично.</target>
</trans-unit>
<trans-unit id="50">
<source>No file was uploaded.</source>
<target>Файлът не беше качен.</target>
</trans-unit>
<trans-unit id="51">
<source>No temporary folder was configured in php.ini.</source>
<target>Не е посочена директория за временни файлове в php.ini.</target>
</trans-unit>
<trans-unit id="52">
<source>Cannot write temporary file to disk.</source>
<target>Не може да запише временен файл на диска.</target>
</trans-unit>
<trans-unit id="53">
<source>A PHP extension caused the upload to fail.</source>
<target>PHP разширение предизвика прекъсване на качването.</target>
</trans-unit>
<trans-unit id="54">
<source>This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.</source>
<target>Колекцията трябва да съдържа поне {{ limit }} елемент.|Колекцията трябва да съдържа поне {{ limit }} елемента.</target>
</trans-unit>
<trans-unit id="55">
<source>This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.</source>
<target>Колекцията трябва да съдържа най-много {{ limit }} елемент.|Колекцията трябва да съдържа най-много {{ limit }} елемента.</target>
</trans-unit>
<trans-unit id="56">
<source>This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.</source>
<target>Колекцията трябва да съдържа точно {{ limit }} елемент.|Колекцията трябва да съдържа точно {{ limit }} елемента.</target>
</trans-unit>
<trans-unit id="57">
<source>Invalid card number.</source>
<target>Невалиден номер на картата.</target>
</trans-unit>
<trans-unit id="58">
<source>Unsupported card type or invalid card number.</source>
<target>Неподдържан тип карта или невалиден номер на картата.</target>
</trans-unit>
<trans-unit id="59">
<source>This is not a valid International Bank Account Number (IBAN).</source>
<target>Невалиден Международен номер на банкова сметка (IBAN).</target>
</trans-unit>
<trans-unit id="60">
<source>This value is not a valid ISBN-10.</source>
<target>Невалиден ISBN-10.</target>
</trans-unit>
<trans-unit id="61">
<source>This value is not a valid ISBN-13.</source>
<target>Невалиден ISBN-13.</target>
</trans-unit>
<trans-unit id="62">
<source>This value is neither a valid ISBN-10 nor a valid ISBN-13.</source>
<target>Невалидна стойност както за ISBN-10, така и за ISBN-13 .</target>
</trans-unit>
<trans-unit id="63">
<source>This value is not a valid ISSN.</source>
<target>Невалиден Международен стандартен сериен номер (ISSN).</target>
</trans-unit>
<trans-unit id="64">
<source>This value is not a valid currency.</source>
<target>Невалидна валута.</target>
</trans-unit>
<trans-unit id="65">
<source>This value should be equal to {{ compared_value }}.</source>
<target>Стойността трябва да бъде равна на {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="66">
<source>This value should be greater than {{ compared_value }}.</source>
<target>Стойността трябва да бъде по-голяма от {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="67">
<source>This value should be greater than or equal to {{ compared_value }}.</source>
<target>Стойността трябва да бъде по-голяма или равна на {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="68">
<source>This value should be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>Стойността трябва да бъде идентична с {{ compared_value_type }} {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="69">
<source>This value should be less than {{ compared_value }}.</source>
<target>Стойността трябва да бъде по-малка {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="70">
<source>This value should be less than or equal to {{ compared_value }}.</source>
<target>Стойността трябва да бъде по-малка или равна на {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="71">
<source>This value should not be equal to {{ compared_value }}.</source>
<target>Стойността не трябва да бъде равна на {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="72">
<source>This value should not be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>Стойността не трябва да бъде идентична с {{ compared_value_type }} {{ compared_value }}.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -306,6 +306,10 @@
<source>The host could not be resolved.</source>
<target>Der Hostname konnte nicht aufgelöst werden.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>Dieser Wert entspricht nicht dem erwarteten Zeichensatz {{ charset }}.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -306,6 +306,10 @@
<source>The host could not be resolved.</source>
<target>The host could not be resolved.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>This value does not match the expected {{ charset }} charset.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -306,6 +306,10 @@
<source>The host could not be resolved.</source>
<target>No se puede resolver el host.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>La codificación de caracteres para este valor debería ser {{ charset }}.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -306,6 +306,10 @@
<source>The host could not be resolved.</source>
<target>Le nom de domaine n'a pas pu être résolu.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>Cette valeur ne correspond pas au jeu de caractères {{ charset }} attendu.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -302,6 +302,10 @@
<source>An empty file is not allowed.</source>
<target>Lege bestanden zijn niet toegestaan.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>Deze waarde is niet in de verwachte tekencodering {{ charset }}.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -306,6 +306,10 @@
<source>The host could not be resolved.</source>
<target>Nazwa hosta nie została rozpoznana.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>Ta wartość nie pasuje do oczekiwanego zestawu znaków {{ charset }}.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -306,6 +306,10 @@
<source>The host could not be resolved.</source>
<target>Имя хоста не может быть разрешено.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>Значение не совпадает с ожидаемой {{ charset }} кодировкой.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -306,6 +306,10 @@
<source>The host could not be resolved.</source>
<target>Gostitelja ni bilo mogoče prepoznati.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>Ta vrednost se ne ujema s pričakovanim naborom znakov {{ charset }}.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -54,12 +54,12 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
return array(
array(12),
array('12'),
array('üü', true),
array('éé', true),
array('üü'),
array('éé'),
array(123),
array('123'),
array('üüü', true),
array('ééé', true),
array('üüü'),
array('ééé'),
);
}
@ -68,8 +68,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
return array(
array(1234),
array('1234'),
array('üüüü', true),
array('éééé', true),
array('üüüü'),
array('éééé'),
);
}
@ -78,24 +78,34 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
return array(
array(12345),
array('12345'),
array('üüüüü', true),
array('ééééé', true),
array('üüüüü'),
array('ééééé'),
array(123456),
array('123456'),
array('üüüüüü', true),
array('éééééé', true),
array('üüüüüü'),
array('éééééé'),
);
}
public function getOneCharset()
{
if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) {
$this->markTestSkipped('Mbstring or iconv is required for this test.');
}
return array(
array("é", "utf8", true),
array("\xE9", "CP1252", true),
array("\xE9", "XXX", false),
array("\xE9", "utf8", false),
);
}
/**
* @dataProvider getFiveOrMoreCharacters
*/
public function testValidValuesMin($value, $mbOnly = false)
public function testValidValuesMin($value)
{
if ($mbOnly && !function_exists('mb_strlen')) {
$this->markTestSkipped('mb_strlen does not exist');
}
$constraint = new Length(array('min' => 5));
$this->validator->validate($value, $constraint);
@ -105,12 +115,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
/**
* @dataProvider getThreeOrLessCharacters
*/
public function testValidValuesMax($value, $mbOnly = false)
public function testValidValuesMax($value)
{
if ($mbOnly && !function_exists('mb_strlen')) {
$this->markTestSkipped('mb_strlen does not exist');
}
$constraint = new Length(array('max' => 3));
$this->validator->validate($value, $constraint);
@ -120,12 +126,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
/**
* @dataProvider getFourCharacters
*/
public function testValidValuesExact($value, $mbOnly = false)
public function testValidValuesExact($value)
{
if ($mbOnly && !function_exists('mb_strlen')) {
$this->markTestSkipped('mb_strlen does not exist');
}
$constraint = new Length(4);
$this->validator->validate($value, $constraint);
@ -135,12 +137,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
/**
* @dataProvider getThreeOrLessCharacters
*/
public function testInvalidValuesMin($value, $mbOnly = false)
public function testInvalidValuesMin($value)
{
if ($mbOnly && !function_exists('mb_strlen')) {
$this->markTestSkipped('mb_strlen does not exist');
}
$constraint = new Length(array(
'min' => 4,
'minMessage' => 'myMessage',
@ -160,12 +158,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
/**
* @dataProvider getFiveOrMoreCharacters
*/
public function testInvalidValuesMax($value, $mbOnly = false)
public function testInvalidValuesMax($value)
{
if ($mbOnly && !function_exists('mb_strlen')) {
$this->markTestSkipped('mb_strlen does not exist');
}
$constraint = new Length(array(
'max' => 4,
'maxMessage' => 'myMessage',
@ -185,12 +179,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
/**
* @dataProvider getThreeOrLessCharacters
*/
public function testInvalidValuesExactLessThanFour($value, $mbOnly = false)
public function testInvalidValuesExactLessThanFour($value)
{
if ($mbOnly && !function_exists('mb_strlen')) {
$this->markTestSkipped('mb_strlen does not exist');
}
$constraint = new Length(array(
'min' => 4,
'max' => 4,
@ -211,12 +201,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
/**
* @dataProvider getFiveOrMoreCharacters
*/
public function testInvalidValuesExactMoreThanFour($value, $mbOnly = false)
public function testInvalidValuesExactMoreThanFour($value)
{
if ($mbOnly && !function_exists('mb_strlen')) {
$this->markTestSkipped('mb_strlen does not exist');
}
$constraint = new Length(array(
'min' => 4,
'max' => 4,
@ -234,6 +220,31 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest
->assertRaised();
}
/**
* @dataProvider getOneCharset
*/
public function testOneCharset($value, $charset, $isValid)
{
$constraint = new Length(array(
'min' => 1,
'max' => 1,
'charset' => $charset,
'charsetMessage' => 'myMessage',
));
$this->validator->validate($value, $constraint);
if ($isValid) {
$this->assertNoViolation();
} else {
$this->buildViolation('myMessage')
->setParameter('{{ value }}', '"'.$value.'"')
->setParameter('{{ charset }}', $charset)
->setInvalidValue($value)
->assertRaised();
}
}
public function testConstraintGetDefaultOption()
{
$constraint = new Length(5);

View File

@ -98,8 +98,6 @@ class StaticMethodLoaderTest extends \PHPUnit_Framework_TestCase
$this->markTestSkipped('Could not disable error reporting');
}
include __DIR__.'/AbstractStaticMethodLoader.php';
$metadata = new ClassMetadata(__NAMESPACE__.'\AbstractStaticMethodLoader');
$loader = new StaticMethodLoader('loadMetadata');

View File

@ -176,7 +176,7 @@ class Data
$cursor->hashCut = $hashCut;
foreach ($children as $key => $child) {
$cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key);
$cursor->hashKey = $cursor->hashKeyIsBinary ? self::utf8Encode($key) : $key;
$cursor->hashKey = $key;
$this->dumpItem($dumper, $cursor, $refs, $child);
if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) {
$parentCursor->stop = true;
@ -191,33 +191,4 @@ class Data
return $hashCut;
}
/**
* Portable variant of utf8_encode()
*
* @param string $s
*
* @return string
*
* @internal
*/
public static function utf8Encode($s)
{
if (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($s, 'UTF-8', 'CP1252');
}
$s .= $s;
$len = strlen($s);
for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) {
switch (true) {
case $s[$i] < "\x80": $s[$j] = $s[$i]; break;
case $s[$i] < "\xC0": $s[$j] = "\xC2"; $s[++$j] = $s[$i]; break;
default: $s[$j] = "\xC3"; $s[++$j] = chr(ord($s[$i]) - 64); break;
}
}
return substr($s, 0, $j);
}
}

View File

@ -97,11 +97,10 @@ class VarCloner extends AbstractCloner
$stub->class = Stub::STRING_BINARY;
if (0 <= $maxString && 0 < $cut = strlen($v) - $maxString) {
$stub->cut = $cut;
$cut = substr_replace($v, '', -$cut);
$stub->value = substr($v, 0, -$cut);
} else {
$cut = $v;
$stub->value = $v;
}
$stub->value = Data::utf8Encode($cut);
} elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = iconv_strlen($v, 'UTF-8') - $maxString) {
$stub = new Stub();
$stub->type = Stub::TYPE_STRING;

View File

@ -29,11 +29,16 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
protected $decimalPoint; // This is locale dependent
protected $indentPad = ' ';
private $charset;
private $charsetConverter;
/**
* @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput.
* @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput.
* @param string $charset The default character encoding to use for non-UTF8 strings.
*/
public function __construct($output = null)
public function __construct($output = null, $charset = null)
{
$this->setCharset($charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8');
$this->decimalPoint = (string) 0.5;
$this->decimalPoint = $this->decimalPoint[1];
$this->setOutput($output ?: static::$defaultOutput);
@ -67,6 +72,43 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
return $prev;
}
/**
* Sets the default character encoding to use for non-UTF8 strings.
*
* @param string $charset The default character encoding to use for non-UTF8 strings.
*
* @return string The previous charset.
*/
public function setCharset($charset)
{
$prev = $this->charset;
$this->charsetConverter = 'fallback';
$charset = strtoupper($charset);
$charset = null === $charset || 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset;
$supported = true;
set_error_handler(function () use (&$supported) {$supported = false;});
if (function_exists('mb_encoding_aliases') && mb_encoding_aliases($charset)) {
$this->charset = $charset;
$this->charsetConverter = 'mbstring';
} elseif (function_exists('iconv')) {
$supported = true;
iconv($charset, 'UTF-8', '');
if ($supported) {
$this->charset = $charset;
$this->charsetConverter = 'iconv';
}
}
if ('fallback' === $this->charsetConverter) {
$this->charset = 'ISO-8859-1';
}
restore_error_handler();
return $prev;
}
/**
* Sets the indentation pad string.
*
@ -131,4 +173,50 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n");
}
}
/**
* Converts a non-UTF-8 string to UTF-8.
*
* @param string $s The non-UTF-8 string to convert.
*
* @return string The string converted to UTF-8.
*/
protected function utf8Encode($s)
{
if ('mbstring' === $this->charsetConverter) {
return mb_convert_encoding($s, 'UTF-8', mb_check_encoding($s, $this->charset) ? $this->charset : '8bit');
}
if ('iconv' === $this->charsetConverter) {
$valid = true;
set_error_handler(function () use (&$valid) {$valid = false;});
$c = iconv($this->charset, 'UTF-8', $s);
restore_error_handler();
if ($valid) {
return $c;
}
}
$s .= $s;
$len = strlen($s);
for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) {
switch (true) {
case $s[$i] < "\x80":
$s[$j] = $s[$i];
break;
case $s[$i] < "\xC0":
$s[$j] = "\xC2";
$s[++$j] = $s[$i];
break;
default:
$s[$j] = "\xC3";
$s[++$j] = chr(ord($s[$i]) - 64);
break;
}
}
return substr($s, 0, $j);
}
}

View File

@ -11,7 +11,6 @@
namespace Symfony\Component\VarDumper\Dumper;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\Cursor;
/**
@ -48,9 +47,9 @@ class CliDumper extends AbstractDumper
/**
* {@inheritdoc}
*/
public function __construct($output = null)
public function __construct($output = null, $charset = null)
{
parent::__construct($output);
parent::__construct($output, $charset);
if ('\\' === DIRECTORY_SEPARATOR && false !== @getenv('ANSICON')) {
// Use only the base 16 xterm colors when using ANSICON
@ -140,8 +139,8 @@ class CliDumper extends AbstractDumper
break;
default:
$attr['value'] = isset($value[0]) && !preg_match('//u', $value) ? Data::utf8Encode($value) : $value;
$value = isset($type[0]) && !preg_match('//u', $type) ? Data::utf8Encode($type) : $type;
$attr['value'] = isset($value[0]) && !preg_match('//u', $value) ? $this->utf8Encode($value) : $value;
$value = isset($type[0]) && !preg_match('//u', $type) ? $this->utf8Encode($type) : $type;
break;
}
@ -157,6 +156,9 @@ class CliDumper extends AbstractDumper
{
$this->dumpKey($cursor);
if ($bin) {
$str = $this->utf8Encode($str);
}
if ('' === $str) {
$this->line .= '""';
$this->dumpLine($cursor->depth);
@ -220,6 +222,9 @@ class CliDumper extends AbstractDumper
{
$this->dumpKey($cursor);
if (!preg_match('//u', $class)) {
$class = $this->utf8Encode($class);
}
if (Cursor::HASH_OBJECT === $type) {
$prefix = 'stdClass' !== $class ? $this->style('note', $class).' {' : '{';
} elseif (Cursor::HASH_RESOURCE === $type) {
@ -279,6 +284,9 @@ class CliDumper extends AbstractDumper
protected function dumpKey(Cursor $cursor)
{
if (null !== $key = $cursor->hashKey) {
if ($cursor->hashKeyIsBinary) {
$key = $this->utf8Encode($key);
}
$attr = array('binary' => $cursor->hashKeyIsBinary);
$bin = $cursor->hashKeyIsBinary ? 'b' : '';
$style = 'key';

View File

@ -31,7 +31,7 @@ class HtmlDumper extends CliDumper
protected $headerIsDumped = false;
protected $lastDepth = -1;
protected $styles = array(
'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap',
'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:100000',
'num' => 'font-weight:bold; color:#1299DA',
'const' => 'font-weight:bold',
'str' => 'font-weight:bold; color:#56DB3A',

View File

@ -24,7 +24,6 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
require __DIR__.'/Fixtures/dumb-var.php';
$dumper = new HtmlDumper('php://output');
$dumper->setColors(false);
$dumper->setDumpHeader('<foo></foo>');
$dumper->setDumpBoundaries('<bar>', '</bar>');
$cloner = new VarCloner();
@ -108,6 +107,36 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
</samp>]
</bar>
EOTXT
,
$out
);
}
public function testCharset()
{
if (!extension_loaded('mbstring')) {
$this->markTestSkipped('This test requires mbstring.');
}
$var = mb_convert_encoding('Словарь', 'CP1251', 'UTF-8');
$dumper = new HtmlDumper('php://output', 'CP1251');
$dumper->setDumpHeader('<foo></foo>');
$dumper->setDumpBoundaries('<bar>', '</bar>');
$cloner = new VarCloner();
$data = $cloner->cloneVar($var);
$out = fopen('php://memory', 'r+b');
$dumper->dump($data, $out);
rewind($out);
$out = stream_get_contents($out);
$this->assertStringMatchesFormat(
<<<EOTXT
<foo></foo><bar>b"<span class=sf-dump-str title="7 binary or non-UTF-8 characters">&#1057;&#1083;&#1086;&#1074;&#1072;&#1088;&#1100;</span>"
</bar>
EOTXT
,