Merge branch '2.1' into 2.2

* 2.1:
  sub-requests are now created with the same class as their parent
  [FrameworkBundle] removed BC break
  [FrameworkBundle] changed temp kernel name in cache:clear
  [DoctrineBridge] Avoids blob values to be logged by doctrine
  [Security] use current request attributes to generate redirect url?
  [Validator] fix showing wrong max file size for upload errors
  [TwigBridge] removed double var initialization (refs #7344)
  [2.1][TwigBridge] Fixes Issue #7342 in TwigBridge
  [FrameworkBundle] fixed cahe:clear command's warmup
  [TwigBridge] now enter/leave scope on Twig_Node_Module
  [TwigBridge] fixed fixed scope & trans_default_domain node visitor
  [TwigBridge] fixed non probant tests & added new one
  [BrowserKit] added ability to ignored malformed set-cookie header
  [Translation] removed wriong 'use'
  [Translation] added xliff loader/dumper with resname support
  [TwigBridge] fixes

Conflicts:
	src/Symfony/Bundle/FrameworkBundle/HttpKernel.php
	src/Symfony/Component/Security/Http/HttpUtils.php
	src/Symfony/Component/Translation/Loader/XliffFileLoader.php
	src/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php
This commit is contained in:
Fabien Potencier 2013-03-15 11:14:31 +01:00
commit 0d7593c636
21 changed files with 457 additions and 103 deletions

View File

@ -22,6 +22,9 @@ use Doctrine\DBAL\Logging\SQLLogger;
*/
class DbalLogger implements SQLLogger
{
const MAX_STRING_LENGTH = 32;
const BINARY_DATA_VALUE = '(binary value)';
protected $logger;
protected $stopwatch;
@ -46,6 +49,26 @@ class DbalLogger implements SQLLogger
$this->stopwatch->start('doctrine', 'doctrine');
}
if (is_array($params)) {
foreach ($params as $index => $param) {
if (!is_string($params[$index])) {
continue;
}
// non utf-8 strings break json encoding
if (null === preg_match('#[^\p{L}\p{N} ]#u', $params[$index])) {
$params[$index] = self::BINARY_DATA_VALUE;
continue;
}
// too long string must be shorten
if (self::MAX_STRING_LENGTH < strlen($params[$index])) {
$params[$index] = substr($params[$index], self::MAX_STRING_LENGTH - 6).' [...]';
continue;
}
}
}
if (null !== $this->logger) {
$this->log($sql, null === $params ? array() : $params);
}

View File

@ -33,10 +33,15 @@ class EntityTypePerformanceTest extends FormPerformanceTestCase
protected function getExtensions()
{
$manager = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
$manager->expects($this->any())
->method('getManager')
->will($this->returnValue($this->em));
$manager->expects($this->any())
->method('getManagerForClass')
->will($this->returnValue($this->em));
return array(
new CoreExtension(),
new DoctrineOrmExtension($manager)

View File

@ -11,6 +11,8 @@
namespace Symfony\Bridge\Doctrine\Tests\Logger;
use Symfony\Bridge\Doctrine\Logger\DbalLogger;
class DbalLoggerTest extends \PHPUnit_Framework_TestCase
{
/**
@ -59,12 +61,38 @@ class DbalLoggerTest extends \PHPUnit_Framework_TestCase
$dbalLogger
->expects($this->once())
->method('log')
->with('SQL', array('utf8' => 'foo', 'nonutf8' => "\x7F\xFF"))
->with('SQL', array('utf8' => 'foo', 'nonutf8' => DbalLogger::BINARY_DATA_VALUE))
;
$dbalLogger->startQuery('SQL', array(
'utf8' => 'foo',
'nonutf8' => "\x7F\xFF"
'nonutf8' => "\x7F\xFF",
));
}
public function testLogLongString()
{
$logger = $this->getMock('Symfony\\Component\\HttpKernel\\Log\\LoggerInterface');
$dbalLogger = $this
->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger')
->setConstructorArgs(array($logger, null))
->setMethods(array('log'))
->getMock()
;
$shortString = str_repeat('a', DbalLogger::MAX_STRING_LENGTH);
$longString = str_repeat('a', DbalLogger::MAX_STRING_LENGTH + 1);
$dbalLogger
->expects($this->once())
->method('log')
->with('SQL', array('short' => $shortString, 'long' => substr($longString, DbalLogger::MAX_STRING_LENGTH - 6).' [...]'))
;
$dbalLogger->startQuery('SQL', array(
'short' => $shortString,
'long' => $longString,
));
}
}

View File

@ -0,0 +1,126 @@
<?php
namespace Symfony\Bridge\Twig\NodeVisitor;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class Scope
{
/**
* @var Scope|null
*/
private $parent;
/**
* @var Scope[]
*/
private $children;
/**
* @var array
*/
private $data;
/**
* @var boolean
*/
private $left;
/**
* @param Scope $parent
*/
public function __construct(Scope $parent = null)
{
$this->parent = $parent;
$this->left = false;
$this->data = array();
}
/**
* Opens a new child scope.
*
* @return Scope
*/
public function enter()
{
$child = new self($this);
$this->children[] = $child;
return $child;
}
/**
* Closes current scope and returns parent one.
*
* @return Scope|null
*/
public function leave()
{
$this->left = true;
return $this->parent;
}
/**
* Stores data into current scope.
*
* @param string $key
* @param mixed $value
*
* @throws \LogicException
*
* @return Scope Current scope
*/
public function set($key, $value)
{
if ($this->left) {
throw new \LogicException('Left scope is not mutable.');
}
$this->data[$key] = $value;
return $this;
}
/**
* Tests if a data is visible from current scope.
*
* @param string $key
*
* @return boolean
*/
public function has($key)
{
if (array_key_exists($key, $this->data)) {
return true;
}
if (null === $this->parent) {
return false;
}
return $this->parent->has($key);
}
/**
* Returns data visible from current scope.
*
* @param string $key
* @param mixed $default
*
* @return mixed
*/
public function get($key, $default = null)
{
if (array_key_exists($key, $this->data)) {
return $this->data[$key];
}
if (null === $this->parent) {
return $default;
}
return $this->parent->get($key, $default);
}
}

View File

@ -21,32 +21,43 @@ use Symfony\Bridge\Twig\Node\TransDefaultDomainNode;
*/
class TranslationDefaultDomainNodeVisitor implements \Twig_NodeVisitorInterface
{
private $domain;
/**
* @var Scope
*/
private $scope;
/**
* Constructor.
*/
public function __construct()
{
$this->scope = new Scope();
}
/**
* {@inheritdoc}
*/
public function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env)
{
if ($node instanceof \Twig_Node_Module) {
$this->domain = null;
if ($node instanceof \Twig_Node_Block || $node instanceof \Twig_Node_Module) {
$this->scope = $this->scope->enter();
}
if ($node instanceof TransDefaultDomainNode) {
if ($node->getNode('expr') instanceof \Twig_Node_Expression_Constant) {
$this->domain = $node->getNode('expr');
$this->scope->set('domain', $node->getNode('expr'));
return $node;
} else {
$var = $env->getParser()->getVarName();
$name = new \Twig_Node_Expression_AssignName($var, $node->getLine());
$this->domain = new \Twig_Node_Expression_Name($var, $node->getLine());
$this->scope->set('domain', new \Twig_Node_Expression_Name($var, $node->getLine()));
return new \Twig_Node_Set(false, new \Twig_Node(array($name)), new \Twig_Node(array($node->getNode('expr'))), $node->getLine());
}
}
if (null === $this->domain) {
if (!$this->scope->has('domain')) {
return $node;
}
@ -58,11 +69,11 @@ class TranslationDefaultDomainNodeVisitor implements \Twig_NodeVisitorInterface
$arguments->setNode($ind - 1, new \Twig_Node_Expression_Array(array(), $node->getLine()));
}
$arguments->setNode($ind, $this->domain);
$arguments->setNode($ind, $this->scope->get('domain'));
}
} elseif ($node instanceof TransNode) {
if (null === $node->getNode('domain')) {
$node->setNode('domain', $this->domain);
$node->setNode('domain', $this->scope->get('domain'));
}
}
@ -78,6 +89,10 @@ class TranslationDefaultDomainNodeVisitor implements \Twig_NodeVisitorInterface
return false;
}
if ($node instanceof \Twig_Node_Block || $node instanceof \Twig_Node_Module) {
$this->scope = $this->scope->leave();
}
return $node;
}

View File

@ -0,0 +1,16 @@
<?php
namespace Symfony\Bridge\Twig\Tests\NodeVisitor;
use Symfony\Bridge\Twig\NodeVisitor\Scope;
use Symfony\Bridge\Twig\Tests\TestCase;
class ScopeTest extends TestCase
{
public function testScopeInitiation()
{
$scope = new Scope();
$scope->enter();
$this->assertNull($scope->get('test'));
}
}

View File

@ -15,11 +15,10 @@ class TranslationDefaultDomainNodeVisitorTest extends TestCase
public function testDefaultDomainAssignment(\Twig_Node $node)
{
$env = new \Twig_Environment(new \Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
$visitor = new TranslationDefaultDomainNodeVisitor();
// visit trans_default_domain tag
$defaultDomain = TwigNodeProvider::getTransDefaultDomainTag('domain');
$defaultDomain = TwigNodeProvider::getTransDefaultDomainTag(self::$domain);
$visitor->enterNode($defaultDomain, $env);
$visitor->leaveNode($defaultDomain, $env);
@ -38,12 +37,38 @@ class TranslationDefaultDomainNodeVisitorTest extends TestCase
$this->assertEquals(array(array(self::$message, self::$domain)), $visitor->getMessages());
}
/** @dataProvider getDefaultDomainAssignmentTestData */
public function testNewModuleWithoutDefaultDomainTag(\Twig_Node $node)
{
$env = new \Twig_Environment(new \Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
$visitor = new TranslationDefaultDomainNodeVisitor();
// visit trans_default_domain tag
$newModule = TwigNodeProvider::getModule('test');
$visitor->enterNode($newModule, $env);
$visitor->leaveNode($newModule, $env);
// visit tested node
$enteredNode = $visitor->enterNode($node, $env);
$leavedNode = $visitor->leaveNode($node, $env);
$this->assertSame($node, $enteredNode);
$this->assertSame($node, $leavedNode);
// extracting tested node messages
$visitor = new TranslationNodeVisitor();
$visitor->enable();
$visitor->enterNode($node, $env);
$visitor->leaveNode($node, $env);
$this->assertEquals(array(array(self::$message, null)), $visitor->getMessages());
}
public function getDefaultDomainAssignmentTestData()
{
return array(
array(TwigNodeProvider::getTransFilter(self::$message, self::$domain)),
array(TwigNodeProvider::getTransChoiceFilter(self::$message, self::$domain)),
array(TwigNodeProvider::getTransTag(self::$message, self::$domain)),
array(TwigNodeProvider::getTransFilter(self::$message)),
array(TwigNodeProvider::getTransChoiceFilter(self::$message)),
array(TwigNodeProvider::getTransTag(self::$message)),
);
}
}

View File

@ -7,6 +7,19 @@ use Symfony\Bridge\Twig\Node\TransNode;
class TwigNodeProvider
{
public static function getModule($content)
{
return new \Twig_Node_Module(
new \Twig_Node_Expression_Constant($content, 0),
null,
new \Twig_Node_Expression_Array(array(), 0),
new \Twig_Node_Expression_Array(array(), 0),
new \Twig_Node_Expression_Array(array(), 0),
null,
null
);
}
public static function getTransFilter($message, $domain = null)
{
$arguments = $domain ? array(

View File

@ -25,8 +25,6 @@ use Symfony\Component\Finder\Finder;
*/
class CacheClearCommand extends ContainerAwareCommand
{
protected $name;
/**
* {@inheritdoc}
*/
@ -57,31 +55,32 @@ EOF
{
$realCacheDir = $this->getContainer()->getParameter('kernel.cache_dir');
$oldCacheDir = $realCacheDir.'_old';
$filesystem = $this->getContainer()->get('filesystem');
if (!is_writable($realCacheDir)) {
throw new \RuntimeException(sprintf('Unable to write in the "%s" directory', $realCacheDir));
}
$filesystem = $this->getContainer()->get('filesystem');
$kernel = $this->getContainer()->get('kernel');
$output->writeln(sprintf('Clearing the cache for the <info>%s</info> environment with debug <info>%s</info>', $kernel->getEnvironment(), var_export($kernel->isDebug(), true)));
$this->getContainer()->get('cache_clearer')->clear($realCacheDir);
if ($filesystem->exists($oldCacheDir)) {
$filesystem->remove($oldCacheDir);
}
$kernel = $this->getContainer()->get('kernel');
$output->writeln(sprintf('Clearing the cache for the <info>%s</info> environment with debug <info>%s</info>', $kernel->getEnvironment(), var_export($kernel->isDebug(), true)));
$this->getContainer()->get('cache_clearer')->clear($realCacheDir);
if ($input->getOption('no-warmup')) {
$filesystem->rename($realCacheDir, $oldCacheDir);
} else {
$warmupDir = $realCacheDir.'_new';
// the warmup cache dir name must have the same length than the real one
// to avoid the many problems in serialized resources files
$warmupDir = substr($realCacheDir, 0, -1).'_';
if ($filesystem->exists($warmupDir)) {
$filesystem->remove($warmupDir);
}
$this->warmup($warmupDir, !$input->getOption('no-optional-warmers'));
$this->warmup($warmupDir, $realCacheDir, !$input->getOption('no-optional-warmers'));
$filesystem->rename($realCacheDir, $oldCacheDir);
$filesystem->rename($warmupDir, $realCacheDir);
@ -90,99 +89,103 @@ EOF
$filesystem->remove($oldCacheDir);
}
protected function warmup($warmupDir, $enableOptionalWarmers = true)
/**
* @param string $warmupDir
* @param string $realCacheDir
* @param bool $enableOptionalWarmers
*/
protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = true)
{
$this->getContainer()->get('filesystem')->remove($warmupDir);
$parent = $this->getContainer()->get('kernel');
$class = get_class($parent);
// create a temporary kernel
$realKernel = $this->getContainer()->get('kernel');
$realKernelClass = get_class($realKernel);
$namespace = '';
if (false !== $pos = strrpos($class, '\\')) {
$namespace = substr($class, 0, $pos);
$class = substr($class, $pos + 1);
if (false !== $pos = strrpos($realKernelClass, '\\')) {
$namespace = substr($realKernelClass, 0, $pos);
$realKernelClass = substr($realKernelClass, $pos + 1);
}
$tempKernel = $this->getTempKernel($realKernel, $namespace, $realKernelClass, $warmupDir);
$tempKernel->boot();
$kernel = $this->getTempKernel($parent, $namespace, $class, $warmupDir);
$kernel->boot();
$warmer = $kernel->getContainer()->get('cache_warmer');
// warmup temporary dir
$warmer = $tempKernel->getContainer()->get('cache_warmer');
if ($enableOptionalWarmers) {
$warmer->enableOptionalWarmers();
}
$warmer->warmUp($warmupDir);
// fix references to the Kernel in .meta files
foreach (Finder::create()->files()->name('*.meta')->in($warmupDir) as $file) {
// fix meta references to the Kernel
$content = preg_replace(
'/C\:\d+\:"'.preg_quote($class.$this->getTempKernelSuffix(), '"/').'"/',
sprintf('C:%s:"%s"', strlen($class), $class),
file_put_contents($file, preg_replace(
'/(C\:\d+\:)"'.get_class($tempKernel).'"/',
sprintf('$1"%s"', $realKernelClass),
file_get_contents($file)
);
// fix meta references to cache files
$realWarmupDir = substr($warmupDir, 0, -4);
$content = preg_replace_callback(
'/s\:\d+\:"'.preg_quote($warmupDir, '/').'([^"]+)"/',
function (array $matches) use ($realWarmupDir) {
$path = $realWarmupDir.$matches[1];
return sprintf('s:%s:"%s"', strlen($path), $path);
},
$content
);
));
}
// fix references to cached files with the real cache directory name
foreach (Finder::create()->files()->in($warmupDir) as $file) {
$content = str_replace($warmupDir, $realCacheDir, file_get_contents($file));
file_put_contents($file, $content);
}
// fix container files and classes
$regex = '/'.preg_quote($this->getTempKernelSuffix(), '/').'/';
foreach (Finder::create()->files()->name(get_class($kernel->getContainer()).'*')->in($warmupDir) as $file) {
$content = file_get_contents($file);
$content = preg_replace($regex, '', $content);
// fix absolute paths to the cache directory
$content = preg_replace('/'.preg_quote($warmupDir, '/').'/', preg_replace('/_new$/', '', $warmupDir), $content);
file_put_contents(preg_replace($regex, '', $file), $content);
// fix references to kernel/container related classes
$search = $tempKernel->getName().ucfirst($tempKernel->getEnvironment());
$replace = $realKernel->getName().ucfirst($realKernel->getEnvironment());
foreach (Finder::create()->files()->name($search.'*')->in($warmupDir) as $file) {
$content = str_replace($search, $replace, file_get_contents($file));
file_put_contents(str_replace($search, $replace, $file), $content);
unlink($file);
}
}
protected function getTempKernelSuffix()
/**
* @deprecated to be removed in 2.3
*/
protected function getTempSuffix()
{
if (null === $this->name) {
$this->name = '__'.uniqid().'__';
}
return $this->name;
return '';
}
protected function getTempKernel(KernelInterface $parent, $namespace, $class, $warmupDir)
/**
* @param KernelInterface $parent
* @param string $namespace
* @param string $parentClass
* @param string $warmupDir
*
* @return KernelInterface
*/
protected function getTempKernel(KernelInterface $parent, $namespace, $parentClass, $warmupDir)
{
$suffix = $this->getTempKernelSuffix();
$rootDir = $parent->getRootDir();
// the temp kernel class name must have the same length than the real one
// to avoid the many problems in serialized resources files
$class = substr($parentClass, 0, -1).'_';
// the temp kernel name must be changed too
$name = substr($parent->getName(), 0, -1).'_';
$code = <<<EOF
<?php
namespace $namespace
{
class $class$suffix extends $class
class $class extends $parentClass
{
public function getCacheDir()
{
return '$warmupDir';
}
public function getName()
{
return '$name';
}
public function getRootDir()
{
return '$rootDir';
}
protected function getContainerClass()
{
return parent::getContainerClass().'$suffix';
}
}
}
EOF;
@ -190,7 +193,7 @@ EOF;
file_put_contents($file = $warmupDir.'/kernel.tmp', $code);
require_once $file;
@unlink($file);
$class = "$namespace\\$class$suffix";
$class = "$namespace\\$class";
return new $class($parent->getEnvironment(), $parent->isDebug());
}

View File

@ -109,7 +109,11 @@ class CookieJar
}
foreach ($cookies as $cookie) {
$this->set(Cookie::fromString($cookie, $uri));
try {
$this->set(Cookie::fromString($cookie, $uri));
} catch (\InvalidArgumentException $e) {
// invalid cookies are just ignored
}
}
}

View File

@ -82,6 +82,13 @@ class CookieJarTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('bar', $cookieJar->get('bar')->getValue(), '->updateFromSetCookie() keeps existing cookies');
}
public function testUpdateFromEmptySetCookie()
{
$cookieJar = new CookieJar();
$cookieJar->updateFromSetCookie(array(''));
$this->assertEquals(array(), $cookieJar->all());
}
public function testUpdateFromSetCookieWithMultipleCookies()
{
$timestamp = time() + 3600;

View File

@ -250,7 +250,7 @@ class Store implements StoreInterface
// As per the RFC, invalidate Location and Content-Location URLs if present
foreach (array('Location', 'Content-Location') as $header) {
if ($uri = $request->headers->get($header)) {
$subRequest = Request::create($uri, 'get', array(), array(), array(), $request->server->all());
$subRequest = call_user_func(array(get_class($request), 'create'), 'get', array(), array(), array(), $request->server->all());
$this->invalidate($subRequest);
}

View File

@ -70,7 +70,7 @@ class HttpUtils
*/
public function createRequest(Request $request, $path)
{
$newRequest = Request::create($this->generateUri($request, $path), 'get', array(), $request->cookies->all(), array(), $request->server->all());
$newRequest = call_user_func(array(get_class($request), 'create'), $this->generateUri($request, $path), 'get', array(), $request->cookies->all(), array(), $request->server->all());
if ($request->hasSession()) {
$newRequest->setSession($request->getSession());
}
@ -140,6 +140,16 @@ class HttpUtils
throw new \LogicException('You must provide a UrlGeneratorInterface instance to be able to use routes.');
}
return $this->urlGenerator->generate($path, array(), UrlGeneratorInterface::ABSOLUTE_URL);
$url = $this->urlGenerator->generate($path, $attributes, UrlGeneratorInterface::ABSOLUTE_URL);
// unnecessary query string parameters must be removed from url
// (ie. query parameters that are presents in $attributes)
// fortunately, they all are, so we have to remove entire query string
$position = strpos($url, '?');
if (false !== $position) {
$url = substr($url, 0, $position);
}
return $url;
}
}

View File

@ -137,13 +137,25 @@ class HttpUtilsTest extends \PHPUnit_Framework_TestCase
$utils->checkRequestPath($this->getRequest(), 'foobar');
}
private function getUrlGenerator()
public function testGenerateUrlRemovesQueryString()
{
$method = new \ReflectionMethod('Symfony\Component\Security\Http\HttpUtils', 'generateUrl');
$method->setAccessible(true);
$utils = new HttpUtils($this->getUrlGenerator());
$this->assertEquals('/foo/bar', $method->invoke($utils, 'route_name'));
$utils = new HttpUtils($this->getUrlGenerator('/foo/bar?param=value'));
$this->assertEquals('/foo/bar', $method->invoke($utils, 'route_name'));
}
private function getUrlGenerator($generatedUrl = '/foo/bar')
{
$urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface');
$urlGenerator
->expects($this->any())
->method('generate')
->will($this->returnValue('/foo/bar'))
->will($this->returnValue($generatedUrl))
;
return $urlGenerator;

View File

@ -27,24 +27,30 @@ class XliffFileDumper extends FileDumper
{
$dom = new \DOMDocument('1.0', 'utf-8');
$dom->formatOutput = true;
$xliff = $dom->appendChild($dom->createElement('xliff'));
$xliff->setAttribute('version', '1.2');
$xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
$xliffFile = $xliff->appendChild($dom->createElement('file'));
$xliffFile->setAttribute('source-language', $messages->getLocale());
$xliffFile->setAttribute('datatype', 'plaintext');
$xliffFile->setAttribute('original', 'file.ext');
$xliffBody = $xliffFile->appendChild($dom->createElement('body'));
$id = 1;
foreach ($messages->all($domain) as $source => $target) {
$trans = $dom->createElement('trans-unit');
$trans->setAttribute('id', $id);
$s = $trans->appendChild($dom->createElement('source'));
$translation = $dom->createElement('trans-unit');
$translation->setAttribute('id', md5($source));
$translation->setAttribute('resname', $source);
$s = $translation->appendChild($dom->createElement('source'));
$s->appendChild($dom->createTextNode($source));
$t = $trans->appendChild($dom->createElement('target'));
$t = $translation->appendChild($dom->createElement('target'));
$t->appendChild($dom->createTextNode($target));
$xliffBody->appendChild($trans);
$id++;
$xliffBody->appendChild($translation);
}
return $dom->saveXML();

View File

@ -45,10 +45,14 @@ class XliffFileLoader implements LoaderInterface
$catalogue = new MessageCatalogue($locale);
foreach ($xml->xpath('//xliff:trans-unit') as $translation) {
if (!isset($translation->source) || !isset($translation->target)) {
$attributes = $translation->attributes();
if (!(isset($attributes['resname']) || isset($translation->source)) || !isset($translation->target)) {
continue;
}
$catalogue->set((string) $translation->source, (string) $translation->target, $domain);
$source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source;
$catalogue->set((string) $source, (string) $translation->target, $domain);
}
$catalogue->addResource(new FileResource($resource));
@ -60,6 +64,8 @@ class XliffFileLoader implements LoaderInterface
*
* @param string $file
*
* @throws \RuntimeException
*
* @return \SimpleXMLElement
*
* @throws InvalidResourceException

View File

@ -33,6 +33,14 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
}
public function testLoadWithResname()
{
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/resname.xlf', 'en', 'domain1');
$this->assertEquals(array('foo' => 'bar', 'bar' => 'baz', 'baz' => 'foo'), $catalogue->all('domain1'));
}
public function testIncompleteResource()
{
$loader = new XliffFileLoader();

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1" resname="foo">
<source></source>
<target>bar</target>
</trans-unit>
<trans-unit id="2" resname="bar">
<source>bar source</source>
<target>baz</target>
</trans-unit>
<trans-unit id="3">
<source>baz</source>
<target>foo</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -2,11 +2,11 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<trans-unit id="acbd18db4cc2f85cedef654fccc4a4d8" resname="foo">
<source>foo</source>
<target>bar</target>
</trans-unit>
<trans-unit id="2">
<trans-unit id="3c6e0b8a9c15224a8228b9a98ca1531d" resname="key">
<source>key</source>
<target></target>
</trans-unit>

View File

@ -37,8 +37,21 @@ class FileValidator extends ConstraintValidator
if ($value instanceof UploadedFile && !$value->isValid()) {
switch ($value->getError()) {
case UPLOAD_ERR_INI_SIZE:
$maxSize = UploadedFile::getMaxFilesize();
$maxSize = $constraint->maxSize ? min($maxSize, $constraint->maxSize) : $maxSize;
if ($constraint->maxSize) {
if (ctype_digit((string) $constraint->maxSize)) {
$maxSize = (int) $constraint->maxSize;
} elseif (preg_match('/^\d++k$/', $constraint->maxSize)) {
$maxSize = $constraint->maxSize * 1024;
} elseif (preg_match('/^\d++M$/', $constraint->maxSize)) {
$maxSize = $constraint->maxSize * 1048576;
} else {
throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum size', $constraint->maxSize));
}
$maxSize = min(UploadedFile::getMaxFilesize(), $maxSize);
} else {
$maxSize = UploadedFile::getMaxFilesize();
}
$this->context->addViolation($constraint->uploadIniSizeErrorMessage, array(
'{{ limit }}' => $maxSize,
'{{ suffix }}' => 'bytes',
@ -97,15 +110,15 @@ class FileValidator extends ConstraintValidator
if ($constraint->maxSize) {
if (ctype_digit((string) $constraint->maxSize)) {
$size = filesize($path);
$limit = $constraint->maxSize;
$limit = (int) $constraint->maxSize;
$suffix = 'bytes';
} elseif (preg_match('/^(\d+)k$/', $constraint->maxSize, $matches)) {
} elseif (preg_match('/^\d++k$/', $constraint->maxSize)) {
$size = round(filesize($path) / 1000, 2);
$limit = $matches[1];
$limit = (int) $constraint->maxSize;
$suffix = 'kB';
} elseif (preg_match('/^(\d+)M$/', $constraint->maxSize, $matches)) {
} elseif (preg_match('/^\d++M$/', $constraint->maxSize)) {
$size = round(filesize($path) / 1000000, 2);
$limit = $matches[1];
$limit = (int) $constraint->maxSize;
$suffix = 'MB';
} else {
throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum size', $constraint->maxSize));

View File

@ -288,12 +288,13 @@ abstract class FileValidatorTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider uploadedFileErrorProvider
*/
public function testUploadedFileError($error, $message, array $params = array())
public function testUploadedFileError($error, $message, array $params = array(), $maxSize = null)
{
$file = new UploadedFile('/path/to/file', 'originalName', 'mime', 0, $error);
$constraint = new File(array(
$message => 'myMessage',
'maxSize' => $maxSize
));
$this->context->expects($this->once())
@ -316,10 +317,24 @@ abstract class FileValidatorTest extends \PHPUnit_Framework_TestCase
);
if (class_exists('Symfony\Component\HttpFoundation\File\UploadedFile')) {
// when no maxSize is specified on constraint, it should use the ini value
$tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
'{{ limit }}' => UploadedFile::getMaxFilesize(),
'{{ suffix }}' => 'bytes',
));
// it should use the smaller limitation (maxSize option in this case)
$tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
'{{ limit }}' => 1,
'{{ suffix }}' => 'bytes',
), '1');
// it correctly parses the maxSize option and not only uses simple string comparison
// 1000M should be bigger than the ini value
$tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
'{{ limit }}' => UploadedFile::getMaxFilesize(),
'{{ suffix }}' => 'bytes',
), '1000M');
}
return $tests;