Merge branch '2.7' into 2.8
* 2.7: [HttpFoundation] Use UPSERT for sessions stored in PgSql >= 9.5 [Console] fixed PHPDoc [travis] HHVM 3.12 LTS Fix feature detection for IE [Form] Fixed collapsed choice attributes [Console] added explanation of messages usage in a progress bar force enabling the external XML entity loaders [Yaml] properly count skipped comment lines Conflicts: src/Symfony/Component/Translation/Loader/XliffFileLoader.php
This commit is contained in:
commit
7fa2eab99f
@ -18,7 +18,7 @@ env:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# Use the newer stack for HHVM as HHVM does not support Precise anymore since a long time and so Precise has an outdated version
|
# Use the newer stack for HHVM as HHVM does not support Precise anymore since a long time and so Precise has an outdated version
|
||||||
- php: hhvm
|
- php: hhvm-3.12
|
||||||
sudo: required
|
sudo: required
|
||||||
dist: trusty
|
dist: trusty
|
||||||
group: edge
|
group: edge
|
||||||
|
@ -180,7 +180,7 @@ class DbalSessionHandler implements \SessionHandlerInterface
|
|||||||
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||||
$updateStmt->execute();
|
$updateStmt->execute();
|
||||||
|
|
||||||
// When MERGE is not supported, like in Postgres, we have to use this approach that can result in
|
// When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
|
||||||
// duplicate key errors when the same session is written simultaneously. We can just catch such an
|
// duplicate key errors when the same session is written simultaneously. We can just catch such an
|
||||||
// error and re-execute the update. This is similar to a serializable transaction with retry logic
|
// error and re-execute the update. This is similar to a serializable transaction with retry logic
|
||||||
// on serialization failures but without the overhead and without possible false positives due to
|
// on serialization failures but without the overhead and without possible false positives due to
|
||||||
@ -224,11 +224,11 @@ class DbalSessionHandler implements \SessionHandlerInterface
|
|||||||
{
|
{
|
||||||
$platform = $this->con->getDatabasePlatform()->getName();
|
$platform = $this->con->getDatabasePlatform()->getName();
|
||||||
|
|
||||||
switch ($platform) {
|
switch (true) {
|
||||||
case 'mysql':
|
case 'mysql' === $platform:
|
||||||
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
||||||
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)";
|
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)";
|
||||||
case 'oracle':
|
case 'oracle' === $platform:
|
||||||
// DUAL is Oracle specific dummy table
|
// DUAL is Oracle specific dummy table
|
||||||
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
|
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
|
||||||
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
||||||
@ -239,8 +239,11 @@ class DbalSessionHandler implements \SessionHandlerInterface
|
|||||||
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
|
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
|
||||||
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
||||||
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;";
|
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;";
|
||||||
case 'sqlite':
|
case 'sqlite' === $platform:
|
||||||
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)";
|
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)";
|
||||||
|
case 'postgresql' === $platform && version_compare($this->con->getServerVersion(), '9.5', '>='):
|
||||||
|
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
||||||
|
"ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->timeCol) = (:data, :time) WHERE $this->idCol = :id";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
id="<?php echo $view->escape($id) ?>" name="<?php echo $view->escape($full_name) ?>"
|
||||||
|
<?php if ($disabled): ?>disabled="disabled" <?php endif ?>
|
||||||
|
<?php foreach ($choice_attr as $k => $v): ?>
|
||||||
|
<?php if ($v === true): ?>
|
||||||
|
<?php printf('%s="%s" ', $view->escape($k), $view->escape($k)) ?>
|
||||||
|
<?php elseif ($v !== false): ?>
|
||||||
|
<?php printf('%s="%s" ', $view->escape($k), $view->escape($v)) ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endforeach ?>
|
@ -8,6 +8,6 @@ $translatorHelper = $view['translator']; // outside of the loop for performance
|
|||||||
<?php echo $formHelper->block($form, 'choice_widget_options', array('choices' => $choice)) ?>
|
<?php echo $formHelper->block($form, 'choice_widget_options', array('choices' => $choice)) ?>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<option value="<?php echo $view->escape($choice->value) ?>" <?php echo $view['form']->block($form, 'attributes', array('attr' => $choice->attr)) ?><?php if ($is_selected($choice->value, $value)): ?> selected="selected"<?php endif?>><?php echo $view->escape(false !== $choice_translation_domain ? $translatorHelper->trans($choice->label, array(), $choice_translation_domain) : $choice->label) ?></option>
|
<option value="<?php echo $view->escape($choice->value) ?>" <?php echo $formHelper->block($form, 'choice_attributes', array('choice_attr' => $choice->attr)) ?><?php if ($is_selected($choice->value, $value)): ?> selected="selected"<?php endif?>><?php echo $view->escape(false !== $choice_translation_domain ? $translatorHelper->trans($choice->label, array(), $choice_translation_domain) : $choice->label) ?></option>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
@ -211,7 +211,7 @@
|
|||||||
var addEventListener;
|
var addEventListener;
|
||||||
|
|
||||||
var el = document.createElement('div');
|
var el = document.createElement('div');
|
||||||
if (!'addEventListener' in el) {
|
if (!('addEventListener' in el)) {
|
||||||
addEventListener = function (element, eventName, callback) {
|
addEventListener = function (element, eventName, callback) {
|
||||||
element.attachEvent('on' + eventName, callback);
|
element.attachEvent('on' + eventName, callback);
|
||||||
};
|
};
|
||||||
|
@ -42,7 +42,7 @@ class ProgressBar
|
|||||||
private $stepWidth;
|
private $stepWidth;
|
||||||
private $percent = 0.0;
|
private $percent = 0.0;
|
||||||
private $formatLineCount;
|
private $formatLineCount;
|
||||||
private $messages;
|
private $messages = array();
|
||||||
private $overwrite = true;
|
private $overwrite = true;
|
||||||
|
|
||||||
private static $formatters;
|
private static $formatters;
|
||||||
@ -140,6 +140,16 @@ class ProgressBar
|
|||||||
return isset(self::$formats[$name]) ? self::$formats[$name] : null;
|
return isset(self::$formats[$name]) ? self::$formats[$name] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates a text with a named placeholder.
|
||||||
|
*
|
||||||
|
* The text is displayed when the progress bar is rendered but only
|
||||||
|
* when the corresponding placeholder is part of the custom format line
|
||||||
|
* (by wrapping the name with %).
|
||||||
|
*
|
||||||
|
* @param string $message The text to associate with the placeholder
|
||||||
|
* @param string $name The name of the placeholder
|
||||||
|
*/
|
||||||
public function setMessage($message, $name = 'message')
|
public function setMessage($message, $name = 'message')
|
||||||
{
|
{
|
||||||
$this->messages[$name] = $message;
|
$this->messages[$name] = $message;
|
||||||
|
@ -19,8 +19,7 @@ namespace Symfony\Component\Console\Helper;
|
|||||||
class TableSeparator extends TableCell
|
class TableSeparator extends TableCell
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param string $value
|
* @param array $options
|
||||||
* @param array $options
|
|
||||||
*/
|
*/
|
||||||
public function __construct(array $options = array())
|
public function __construct(array $options = array())
|
||||||
{
|
{
|
||||||
|
@ -73,21 +73,33 @@ class NullOutput implements OutputInterface
|
|||||||
return self::VERBOSITY_QUIET;
|
return self::VERBOSITY_QUIET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function isQuiet()
|
public function isQuiet()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function isVerbose()
|
public function isVerbose()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function isVeryVerbose()
|
public function isVeryVerbose()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function isDebug()
|
public function isDebug()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -94,21 +94,33 @@ abstract class Output implements OutputInterface
|
|||||||
return $this->verbosity;
|
return $this->verbosity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function isQuiet()
|
public function isQuiet()
|
||||||
{
|
{
|
||||||
return self::VERBOSITY_QUIET === $this->verbosity;
|
return self::VERBOSITY_QUIET === $this->verbosity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function isVerbose()
|
public function isVerbose()
|
||||||
{
|
{
|
||||||
return self::VERBOSITY_VERBOSE <= $this->verbosity;
|
return self::VERBOSITY_VERBOSE <= $this->verbosity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function isVeryVerbose()
|
public function isVeryVerbose()
|
||||||
{
|
{
|
||||||
return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
|
return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function isDebug()
|
public function isDebug()
|
||||||
{
|
{
|
||||||
return self::VERBOSITY_DEBUG <= $this->verbosity;
|
return self::VERBOSITY_DEBUG <= $this->verbosity;
|
||||||
|
@ -338,7 +338,6 @@ class XmlFileLoader extends FileLoader
|
|||||||
foreach ($definitions as $id => $def) {
|
foreach ($definitions as $id => $def) {
|
||||||
list($domElement, $file, $wild) = $def;
|
list($domElement, $file, $wild) = $def;
|
||||||
|
|
||||||
|
|
||||||
if (null !== $definition = $this->parseDefinition($domElement, $file)) {
|
if (null !== $definition = $this->parseDefinition($domElement, $file)) {
|
||||||
$this->container->setDefinition($id, $definition);
|
$this->container->setDefinition($id, $definition);
|
||||||
}
|
}
|
||||||
@ -508,7 +507,9 @@ $imports
|
|||||||
EOF
|
EOF
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$disableEntities = libxml_disable_entity_loader(false);
|
||||||
$valid = @$dom->schemaValidateSource($source);
|
$valid = @$dom->schemaValidateSource($source);
|
||||||
|
libxml_disable_entity_loader($disableEntities);
|
||||||
|
|
||||||
foreach ($tmpfiles as $tmpfile) {
|
foreach ($tmpfiles as $tmpfile) {
|
||||||
@unlink($tmpfile);
|
@unlink($tmpfile);
|
||||||
|
@ -84,6 +84,19 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertInstanceOf('DOMDocument', $xml, '->parseFileToDOM() returns an SimpleXMLElement object');
|
$this->assertInstanceOf('DOMDocument', $xml, '->parseFileToDOM() returns an SimpleXMLElement object');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testLoadWithExternalEntitiesDisabled()
|
||||||
|
{
|
||||||
|
$disableEntities = libxml_disable_entity_loader(true);
|
||||||
|
|
||||||
|
$containerBuilder = new ContainerBuilder();
|
||||||
|
$loader = new XmlFileLoader($containerBuilder, new FileLocator(self::$fixturesPath.'/xml'));
|
||||||
|
$loader->load('services2.xml');
|
||||||
|
|
||||||
|
libxml_disable_entity_loader($disableEntities);
|
||||||
|
|
||||||
|
$this->assertTrue(count($containerBuilder->getParameterBag()->all()) > 0, 'Parameters can be read from the config file.');
|
||||||
|
}
|
||||||
|
|
||||||
public function testLoadParameters()
|
public function testLoadParameters()
|
||||||
{
|
{
|
||||||
$container = new ContainerBuilder();
|
$container = new ContainerBuilder();
|
||||||
|
@ -232,6 +232,68 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSingleChoiceAttributesWithMainAttributes()
|
||||||
|
{
|
||||||
|
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||||
|
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||||
|
'choices_as_values' => true,
|
||||||
|
'multiple' => false,
|
||||||
|
'expanded' => false,
|
||||||
|
'attr' => array('class' => 'bar&baz'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'bar&baz')),
|
||||||
|
'/select
|
||||||
|
[@name="name"]
|
||||||
|
[@class="bar&baz form-control"]
|
||||||
|
[not(@required)]
|
||||||
|
[
|
||||||
|
./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
|
||||||
|
/following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
|
||||||
|
]
|
||||||
|
[count(./option)=2]
|
||||||
|
'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSingleExpandedChoiceAttributesWithMainAttributes()
|
||||||
|
{
|
||||||
|
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||||
|
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||||
|
'choices_as_values' => true,
|
||||||
|
'multiple' => false,
|
||||||
|
'expanded' => true,
|
||||||
|
'attr' => array('class' => 'bar&baz'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'bar&baz')),
|
||||||
|
'/div
|
||||||
|
[@class="bar&baz"]
|
||||||
|
[
|
||||||
|
./div
|
||||||
|
[@class="radio"]
|
||||||
|
[
|
||||||
|
./label
|
||||||
|
[.=" [trans]Choice&A[/trans]"]
|
||||||
|
[
|
||||||
|
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
/following-sibling::div
|
||||||
|
[@class="radio"]
|
||||||
|
[
|
||||||
|
./label
|
||||||
|
[.=" [trans]Choice&B[/trans]"]
|
||||||
|
[
|
||||||
|
./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||||
|
]
|
||||||
|
'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function testSelectWithSizeBiggerThanOneCanBeRequired()
|
public function testSelectWithSizeBiggerThanOneCanBeRequired()
|
||||||
{
|
{
|
||||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array(
|
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array(
|
||||||
|
@ -641,6 +641,55 @@ abstract class AbstractLayoutTest extends \Symfony\Component\Form\Test\FormInteg
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSingleChoiceAttributesWithMainAttributes()
|
||||||
|
{
|
||||||
|
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||||
|
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||||
|
'choices_as_values' => true,
|
||||||
|
'multiple' => false,
|
||||||
|
'expanded' => false,
|
||||||
|
'attr' => array('class' => 'bar&baz'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'bar&baz')),
|
||||||
|
'/select
|
||||||
|
[@name="name"]
|
||||||
|
[@class="bar&baz"]
|
||||||
|
[not(@required)]
|
||||||
|
[
|
||||||
|
./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
|
||||||
|
/following-sibling::option[@value="&b"][not(@class)][not(@selected)][.="[trans]Choice&B[/trans]"]
|
||||||
|
]
|
||||||
|
[count(./option)=2]
|
||||||
|
'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSingleExpandedChoiceAttributesWithMainAttributes()
|
||||||
|
{
|
||||||
|
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||||
|
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||||
|
'choices_as_values' => true,
|
||||||
|
'multiple' => false,
|
||||||
|
'expanded' => true,
|
||||||
|
'attr' => array('class' => 'bar&baz'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'bar&baz')),
|
||||||
|
'/div
|
||||||
|
[@class="bar&baz"]
|
||||||
|
[
|
||||||
|
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||||
|
/following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"]
|
||||||
|
/following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||||
|
/following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"]
|
||||||
|
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||||
|
]
|
||||||
|
[count(./input)=3]
|
||||||
|
'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function testSingleChoiceWithPreferred()
|
public function testSingleChoiceWithPreferred()
|
||||||
{
|
{
|
||||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
|
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
|
||||||
|
@ -347,7 +347,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||||||
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||||
$updateStmt->execute();
|
$updateStmt->execute();
|
||||||
|
|
||||||
// When MERGE is not supported, like in Postgres, we have to use this approach that can result in
|
// When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
|
||||||
// duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior).
|
// duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior).
|
||||||
// We can just catch such an error and re-execute the update. This is similar to a serializable
|
// We can just catch such an error and re-execute the update. This is similar to a serializable
|
||||||
// transaction with retry logic on serialization failures but without the overhead and without possible
|
// transaction with retry logic on serialization failures but without the overhead and without possible
|
||||||
@ -659,11 +659,11 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||||||
*/
|
*/
|
||||||
private function getMergeSql()
|
private function getMergeSql()
|
||||||
{
|
{
|
||||||
switch ($this->driver) {
|
switch (true) {
|
||||||
case 'mysql':
|
case 'mysql' === $this->driver:
|
||||||
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
||||||
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
|
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
|
||||||
case 'oci':
|
case 'oci' === $this->driver:
|
||||||
// DUAL is Oracle specific dummy table
|
// DUAL is Oracle specific dummy table
|
||||||
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
|
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
|
||||||
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
||||||
@ -674,8 +674,11 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
|||||||
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
|
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
|
||||||
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
||||||
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time;";
|
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time;";
|
||||||
case 'sqlite':
|
case 'sqlite' === $this->driver:
|
||||||
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
|
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
|
||||||
|
case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='):
|
||||||
|
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
||||||
|
"ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (:data, :lifetime, :time) WHERE $this->idCol = :id";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,10 +173,16 @@ class XliffFileLoader implements LoaderInterface
|
|||||||
{
|
{
|
||||||
$internalErrors = libxml_use_internal_errors(true);
|
$internalErrors = libxml_use_internal_errors(true);
|
||||||
|
|
||||||
|
$disableEntities = libxml_disable_entity_loader(false);
|
||||||
|
|
||||||
if (!@$dom->schemaValidateSource($schema)) {
|
if (!@$dom->schemaValidateSource($schema)) {
|
||||||
|
libxml_disable_entity_loader($disableEntities);
|
||||||
|
|
||||||
throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s', $file, implode("\n", $this->getXmlErrors($internalErrors))));
|
throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s', $file, implode("\n", $this->getXmlErrors($internalErrors))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
libxml_disable_entity_loader($disableEntities);
|
||||||
|
|
||||||
$dom->normalizeDocument();
|
$dom->normalizeDocument();
|
||||||
|
|
||||||
libxml_clear_errors();
|
libxml_clear_errors();
|
||||||
|
@ -46,6 +46,20 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
libxml_use_internal_errors($internalErrors);
|
libxml_use_internal_errors($internalErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testLoadWithExternalEntitiesDisabled()
|
||||||
|
{
|
||||||
|
$disableEntities = libxml_disable_entity_loader(true);
|
||||||
|
|
||||||
|
$loader = new XliffFileLoader();
|
||||||
|
$resource = __DIR__.'/../fixtures/resources.xlf';
|
||||||
|
$catalogue = $loader->load($resource, 'en', 'domain1');
|
||||||
|
|
||||||
|
libxml_disable_entity_loader($disableEntities);
|
||||||
|
|
||||||
|
$this->assertEquals('en', $catalogue->getLocale());
|
||||||
|
$this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
|
||||||
|
}
|
||||||
|
|
||||||
public function testLoadWithResname()
|
public function testLoadWithResname()
|
||||||
{
|
{
|
||||||
$loader = new XliffFileLoader();
|
$loader = new XliffFileLoader();
|
||||||
|
@ -30,17 +30,21 @@ class Parser
|
|||||||
private $currentLineNb = -1;
|
private $currentLineNb = -1;
|
||||||
private $currentLine = '';
|
private $currentLine = '';
|
||||||
private $refs = array();
|
private $refs = array();
|
||||||
|
private $skippedLineNumbers = array();
|
||||||
|
private $locallySkippedLineNumbers = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param int $offset The offset of YAML document (used for line numbers in error messages)
|
* @param int $offset The offset of YAML document (used for line numbers in error messages)
|
||||||
* @param int|null $totalNumberOfLines The overall number of lines being parsed
|
* @param int|null $totalNumberOfLines The overall number of lines being parsed
|
||||||
|
* @param int[] $skippedLineNumbers Number of comment lines that have been skipped by the parser
|
||||||
*/
|
*/
|
||||||
public function __construct($offset = 0, $totalNumberOfLines = null)
|
public function __construct($offset = 0, $totalNumberOfLines = null, array $skippedLineNumbers = array())
|
||||||
{
|
{
|
||||||
$this->offset = $offset;
|
$this->offset = $offset;
|
||||||
$this->totalNumberOfLines = $totalNumberOfLines;
|
$this->totalNumberOfLines = $totalNumberOfLines;
|
||||||
|
$this->skippedLineNumbers = $skippedLineNumbers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,25 +105,18 @@ class Parser
|
|||||||
|
|
||||||
// array
|
// array
|
||||||
if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
|
if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
|
||||||
$c = $this->getRealCurrentLineNb() + 1;
|
$data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap);
|
||||||
$parser = new self($c, $this->totalNumberOfLines);
|
|
||||||
$parser->refs = &$this->refs;
|
|
||||||
$data[] = $parser->parse($this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap);
|
|
||||||
} else {
|
} else {
|
||||||
if (isset($values['leadspaces'])
|
if (isset($values['leadspaces'])
|
||||||
&& preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)
|
&& preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)
|
||||||
) {
|
) {
|
||||||
// this is a compact notation element, add to next block and parse
|
// this is a compact notation element, add to next block and parse
|
||||||
$c = $this->getRealCurrentLineNb();
|
|
||||||
$parser = new self($c, $this->totalNumberOfLines);
|
|
||||||
$parser->refs = &$this->refs;
|
|
||||||
|
|
||||||
$block = $values['value'];
|
$block = $values['value'];
|
||||||
if ($this->isNextLineIndented()) {
|
if ($this->isNextLineIndented()) {
|
||||||
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1);
|
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport, $objectForMap);
|
$data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $exceptionOnInvalidType, $objectSupport, $objectForMap);
|
||||||
} else {
|
} else {
|
||||||
$data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap, $context);
|
$data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap, $context);
|
||||||
}
|
}
|
||||||
@ -175,10 +172,7 @@ class Parser
|
|||||||
} else {
|
} else {
|
||||||
$value = $this->getNextEmbedBlock();
|
$value = $this->getNextEmbedBlock();
|
||||||
}
|
}
|
||||||
$c = $this->getRealCurrentLineNb() + 1;
|
$parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $exceptionOnInvalidType, $objectSupport, $objectForMap);
|
||||||
$parser = new self($c, $this->totalNumberOfLines);
|
|
||||||
$parser->refs = &$this->refs;
|
|
||||||
$parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap);
|
|
||||||
|
|
||||||
if (!is_array($parsed)) {
|
if (!is_array($parsed)) {
|
||||||
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
|
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
|
||||||
@ -226,10 +220,7 @@ class Parser
|
|||||||
$data[$key] = null;
|
$data[$key] = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$c = $this->getRealCurrentLineNb() + 1;
|
$value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap);
|
||||||
$parser = new self($c, $this->totalNumberOfLines);
|
|
||||||
$parser->refs = &$this->refs;
|
|
||||||
$value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap);
|
|
||||||
// Spec: Keys MUST be unique; first one wins.
|
// Spec: Keys MUST be unique; first one wins.
|
||||||
// But overwriting is allowed when a merge node is used in current block.
|
// But overwriting is allowed when a merge node is used in current block.
|
||||||
if ($allowOverwrite || !isset($data[$key])) {
|
if ($allowOverwrite || !isset($data[$key])) {
|
||||||
@ -323,6 +314,24 @@ class Parser
|
|||||||
return empty($data) ? null : $data;
|
return empty($data) ? null : $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function parseBlock($offset, $yaml, $exceptionOnInvalidType, $objectSupport, $objectForMap)
|
||||||
|
{
|
||||||
|
$skippedLineNumbers = $this->skippedLineNumbers;
|
||||||
|
|
||||||
|
foreach ($this->locallySkippedLineNumbers as $lineNumber) {
|
||||||
|
if ($lineNumber < $offset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skippedLineNumbers[] = $lineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers);
|
||||||
|
$parser->refs = &$this->refs;
|
||||||
|
|
||||||
|
return $parser->parse($yaml, $exceptionOnInvalidType, $objectSupport, $objectForMap);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current line number (takes the offset into account).
|
* Returns the current line number (takes the offset into account).
|
||||||
*
|
*
|
||||||
@ -330,7 +339,17 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
private function getRealCurrentLineNb()
|
private function getRealCurrentLineNb()
|
||||||
{
|
{
|
||||||
return $this->currentLineNb + $this->offset;
|
$realCurrentLineNumber = $this->currentLineNb + $this->offset;
|
||||||
|
|
||||||
|
foreach ($this->skippedLineNumbers as $skippedLineNumber) {
|
||||||
|
if ($skippedLineNumber > $realCurrentLineNumber) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++$realCurrentLineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $realCurrentLineNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -432,7 +451,15 @@ class Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we ignore "comment" lines only when we are not inside a scalar block
|
// we ignore "comment" lines only when we are not inside a scalar block
|
||||||
if (empty($blockScalarIndentations) && $this->isCurrentLineComment() && false === $this->checkIfPreviousNonCommentLineIsCollectionItem()) {
|
if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) {
|
||||||
|
// remember ignored comment lines (they are used later in nested
|
||||||
|
// parser calls to determine real line numbers)
|
||||||
|
//
|
||||||
|
// CAUTION: beware to not populate the global property here as it
|
||||||
|
// will otherwise influence the getRealCurrentLineNb() call here
|
||||||
|
// for consecutive comment lines and subsequent embedded blocks
|
||||||
|
$this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,44 +829,4 @@ class Parser
|
|||||||
{
|
{
|
||||||
return (bool) preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
|
return (bool) preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the current line is a collection item.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function isCurrentLineCollectionItem()
|
|
||||||
{
|
|
||||||
$ltrimmedLine = ltrim($this->currentLine, ' ');
|
|
||||||
|
|
||||||
return '' !== $ltrimmedLine && '-' === $ltrimmedLine[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests whether the current comment line is in a collection.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function checkIfPreviousNonCommentLineIsCollectionItem()
|
|
||||||
{
|
|
||||||
$isCollectionItem = false;
|
|
||||||
$moves = 0;
|
|
||||||
while ($this->moveToPreviousLine()) {
|
|
||||||
++$moves;
|
|
||||||
// If previous line is a comment, move back again.
|
|
||||||
if ($this->isCurrentLineComment()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$isCollectionItem = $this->isCurrentLineCollectionItem();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move parser back to previous line.
|
|
||||||
while ($moves > 0) {
|
|
||||||
$this->moveToNextLine();
|
|
||||||
--$moves;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $isCollectionItem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -656,6 +656,25 @@ EOT;
|
|||||||
$this->assertSame($expected, $this->parser->parse($yaml));
|
$this->assertSame($expected, $this->parser->parse($yaml));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSequenceFollowedByCommentEmbeddedInMapping()
|
||||||
|
{
|
||||||
|
$yaml = <<<EOT
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
- c
|
||||||
|
# comment
|
||||||
|
d: e
|
||||||
|
EOT;
|
||||||
|
$expected = array(
|
||||||
|
'a' => array(
|
||||||
|
'b' => array('c'),
|
||||||
|
'd' => 'e',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame($expected, $this->parser->parse($yaml));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
|
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user