bug #11436 fix signal handling in wait() on calls to stop() (xabbuh, romainneutron)

This PR was merged into the 2.3 branch.

Discussion
----------

fix signal handling in wait() on calls to stop()

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #11286
| License       | MIT
| Doc PR        |

``wait()`` throws an exception when the process was terminated by a signal. This should not happen when the termination was requested by calling the ``stop()`` method (for example, inside a callback which is passed to ``wait()``).

Commits
-------

5939d34 [Process] Fix unit tests in sigchild environment
eb68662 [Process] fix signal handling in wait()
94ffc4f bug #11469  [BrowserKit] Fixed server HTTP_HOST port uri conversion (bcremer, fabpot)
103fd88 [BrowserKit] refactor code and fix unquoted regex
f401ab9 Fixed server HTTP_HOST port uri conversion
045cbc5 bug #11425 Fix issue described in #11421 (Ben, ben-rosio)
f5bfa9b bug #11423 Pass a Scope instance instead of a scope name when cloning a container in the GrahpvizDumper (jakzal)
3177be5 minor #11464 [Translator] Use quote to surround invalid locale (lyrixx)
c9742ef [Translator] Use quote to surround invalid locale
4dbe0e1 bug #11120 [2.3][Process] Reduce I/O load on Windows platform (romainneutron)
797d814 bug #11342 [2.3][Form] Check if IntlDateFormatter constructor returned a valid object before using it (romainneutron)
0b5348e minor #11441 [Translator] Optimize assertLocale regexp (Jérémy Derussé)
537c39b Optimize assertLocale regexp
4cf50e8 Bring code into standard
9f4313c [Process] Add test to verify fix for issue #11421
02eb765 [Process] Fixes issue #11421
6787669 [DependencyInjection] Pass a Scope instance instead of a scope name.
9572918 bug #11411 [Validator] Backported #11410 to 2.3: Object initializers are called only once per object (webmozart)
291cbf9 [Validator] Backported #11410 to 2.3: Object initializers are called only once per object
efab884 bug #11403 [Translator][FrameworkBundle] Added @ to the list of allowed chars in Translator (takeit)
3176f8b [Translator][FrameworkBundle] Added @ to the list of allowed chars in Translator
91e32f8 bug #11381 [2.3] [Process] Use correct test for empty string in UnixPipes (whs, romainneutron)
45df2f3 minor #11397 [2.3][Process] Fix unit tests on Windows platform (romainneutron)
cec0a45 [Process] Adjust PR #11264, make it Windows compatible and fix CS
d418935 [Process] Fix unit tests on Windows platform
ff0bb01 [Process] Reduce I/O load on Windows platform
ace5a29 bumped Symfony version to 2.3.19
75e07e6 updated VERSION for 2.3.18
4a12f4d update CONTRIBUTORS for 2.3.18
98b891d updated CHANGELOG for 2.3.18
06a80fb Validate locales sets intos translator
06fc97e feature #11367 [HttpFoundation] Fix to prevent magic bytes injection in JSONP responses... (CVE-2014-4671) (Andrew Moore)
3c54659 minor #11387 [2.3] [Validator] Fix UserPassword validator translation (redstar504)
73d50ed Fix UserPassword validator translation
93a970c bug #11386 Remove Spaceless Blocks from Twig Form Templates (chrisguitarguy)
8f9ed3e Remove Spaceless Blocks from Twig Form Templates
9e1ea4a [Process] Use correct test for empty string in UnixPipes
6af3d05 [HttpFoundation] Fix to prevent magic bytes injection in JSONP responses (Prevents CVE-2014-4671)
ebf967d [Form] Check if IntlDateFormatter constructor returned a valid object before using it
This commit is contained in:
Romain Neutron 2014-07-25 11:23:56 +02:00
commit c548bd861a
32 changed files with 744 additions and 327 deletions

View File

@ -7,6 +7,14 @@ in 2.3 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.3.0...v2.3.1
* 2.3.18 (2014-07-15)
* [Security] Forced validate of locales passed to the translator
* feature #11367 [HttpFoundation] Fix to prevent magic bytes injection in JSONP responses... (CVE-2014-4671) (Andrew Moore)
* bug #11386 Remove Spaceless Blocks from Twig Form Templates (chrisguitarguy)
* bug #9719 [TwigBundle] fix configuration tree for paths (mdavis1982, cordoval)
* bug #11244 [HttpFoundation] Remove body-related headers when sending the response, if body is empty (SimonSimCity)
* 2.3.17 (2014-07-07)
* bug #11238 [Translation] Added unescaping of ids in PoFileLoader (JustBlackBird)

View File

@ -19,8 +19,8 @@ Symfony2 is the result of the work of many people who made the code better
- Ryan Weaver (weaverryan)
- Lukas Kahwe Smith (lsmith)
- Jeremy Mikola (jmikola)
- Jean-François Simon (jfsimon)
- Romain Neutron (romain)
- Jean-François Simon (jfsimon)
- Benjamin Eberlei (beberlei)
- Igor Wiedler (igorw)
- Hugo Hamon (hhamon)
@ -44,18 +44,18 @@ Symfony2 is the result of the work of many people who made the code better
- Wouter De Jong (wouterj)
- Eric Clemmons (ericclemmons)
- Nicolas Grekas (nicolas-grekas)
- Andrej Hudec (pulzarraider)
- Deni
- Henrik Westphal (snc)
- Dariusz Górecki (canni)
- Andrej Hudec (pulzarraider)
- Arnout Boks (aboks)
- Christian Raue
- Michel Weimerskirch (mweimerskirch)
- Lee McDermott
- Brandon Turner
- Douglas Greenshields (shieldo)
- Daniel Holmes (dholmes)
- Jordan Alliot (jalliot)
- Christian Raue
- John Wards (johnwards)
- Fran Moreno (franmomu)
- Bart van den Burg (burgov)
@ -75,21 +75,21 @@ Symfony2 is the result of the work of many people who made the code better
- Fabien Pennequin (fabienpennequin)
- Jacob Dreesen (jdreesen)
- Gábor Egyed (1ed)
- Ait Boudad Abdellatif (aitboudad)
- Adrien Brault (adrienbrault)
- Michal Piotrowski (eventhorizon)
- Ait Boudad Abdellatif (aitboudad)
- Robert Schönthal (digitalkaoz)
- Juti Noppornpitak (shiroyuki)
- Sebastian Hörl (blogsh)
- Daniel Gomes (danielcsgomes)
- Hidenori Goto (hidenorigoto)
- Peter Kokot (maastermedia)
- Christian Flothmann (xabbuh)
- Jérémie Augustin (jaugustin)
- David Buchmann (dbu)
- Jérôme Tamarelle (gromnan)
- Tigran Azatyan (tigranazatyan)
- Javier Eguiluz (javier.eguiluz)
- Christian Flothmann (xabbuh)
- Rafael Dohms (rdohms)
- Richard Shank (iampersistent)
- Gordon Franke (gimler)
@ -146,6 +146,7 @@ Symfony2 is the result of the work of many people who made the code better
- Manuel Reinhard (sprain)
- Danny Berger (dpb587)
- Xavier Montaña Carreras (xmontana)
- Michele Orselli (orso)
- Xavier Perez
- Arjen Brouwer (arjenjb)
- Katsuhiro OGAWA
@ -168,7 +169,6 @@ Symfony2 is the result of the work of many people who made the code better
- Robert Kiss (kepten)
- Kim Hemsø Rasmussen (kimhemsoe)
- Wouter Van Hecke
- Michele Orselli (orso)
- Michael Holm (hollo)
- Marc Weistroff (futurecat)
- Pierre-Yves LEBECQ (pylebecq)
@ -231,6 +231,7 @@ Symfony2 is the result of the work of many people who made the code better
- Ismael Ambrosi (iambrosi)
- Shein Alexey
- Issei Murasawa (issei_m)
- hacfi (hifi)
- Joe Lencioni
- Kai
- Xavier HAUSHERR
@ -274,6 +275,7 @@ Symfony2 is the result of the work of many people who made the code better
- François-Xavier de Guillebon (de-gui_f)
- boombatower
- Fabrice Bernhard (fabriceb)
- Clément Gautier (clementgautier)
- Fabian Lange (codingfabian)
- Yoshio HANAWA
- Baptiste Clavié (talus)
@ -294,11 +296,11 @@ Symfony2 is the result of the work of many people who made the code better
- Maks Slesarenko
- Markus Lanthaler (lanthaler)
- Vicent Soria Durá (vicentgodella)
- Chris Wilkinson (thewilkybarkid)
- Ioan Negulescu
- Andrew Udvare (audvare)
- alexpods
- Erik Trapman (eriktrapman)
- hacfi (hifi)
- De Cock Xavier (xdecock)
- Alex Pott
- Norbert Orzechowicz (norzechowicz)
@ -365,6 +367,7 @@ Symfony2 is the result of the work of many people who made the code better
- Arturs Vonda
- Sascha Grossenbacher
- Ben Davies (bendavies)
- Simon Schick (simonsimcity)
- Hossein Bukhamsin
- Paweł Wacławczyk (pwc)
- Oleg Zinchenko (cystbear)
@ -372,6 +375,7 @@ Symfony2 is the result of the work of many people who made the code better
- Johannes Klauss (cloppy)
- Evan Villemez
- fzerorubigd
- Benjamin Grandfond (benjamin)
- Tiago Brito (blackmx)
- Richard van den Brand (ricbra)
- develop
@ -403,7 +407,6 @@ Symfony2 is the result of the work of many people who made the code better
- Marek Štípek (maryo)
- John Bohn (jbohn)
- Jakub Škvára (jskvara)
- Chris Wilkinson (thewilkybarkid)
- Andrew Hilobok (hilobok)
- Christian Soronellas (theunic)
- Jérôme Vieilledent (lolautruche)
@ -532,7 +535,6 @@ Symfony2 is the result of the work of many people who made the code better
- jfcixmedia
- Martijn Evers
- Benjamin Paap (benjaminpaap)
- Simon Schick (simonsimcity)
- Christian
- Sergii Smertin (nfx)
- Eddie Jaoude
@ -550,6 +552,7 @@ Symfony2 is the result of the work of many people who made the code better
- Benoit Garret
- DerManoMann
- Asmir Mustafic (goetas)
- Julien Bianchi (jubianchi)
- Marcin Chwedziak
- Roland Franssen (ro0)
- Maciej Malarz
@ -562,20 +565,22 @@ Symfony2 is the result of the work of many people who made the code better
- kaiwa
- Albert Ganiev (helios-ag)
- Neil Katin
- David Otton
- peter
- Artem Kolesnikov (tyomo4ka)
- Gustavo Adrian
- Clément Gautier (clementgautier)
- Yannick
- Luc Vieillescazes (iamluc)
- Eduardo García Sanz (coma)
- David de Boer (ddeboer)
- Brooks Boyd
- Roger Webb
- Dmitriy Simushev
- Max Voloshin (maxvoloshin)
- Nicolas Fabre (nfabre)
- Raul Rodriguez (raul782)
- Patrick Landolt (scube)
- WybrenKoelmans
- Derek Lambert
- Felicitus
- Krzysztof Przybyszewski
@ -730,6 +735,7 @@ Symfony2 is the result of the work of many people who made the code better
- catch
- Alexandre Segura
- Josef Cech
- Nate (frickenate)
- Matthew Foster (mfoster)
- Maximilian Reichel (phramz)
- Paul Seiffert (seiffert)
@ -750,7 +756,6 @@ Symfony2 is the result of the work of many people who made the code better
- Andrey Ryaguzov
- Gunther Konig
- František Bereň
- Benjamin Grandfond (benjamin)
- Christoph Nissle (derstoffel)
- Ionel Scutelnicu (ionelscutelnicu)
- Johnny Peck (johnnypeck)
@ -875,6 +880,7 @@ Symfony2 is the result of the work of many people who made the code better
- Yorkie Chadwick (yorkie76)
- Yanick Witschi
- Ondrej Mirtes
- akimsko
- Youpie
- srsbiz
- Nicolas A. Bérard-Nault
@ -1010,6 +1016,7 @@ Symfony2 is the result of the work of many people who made the code better
- Adam Monsen (meonkeys)
- ollie harridge (ollietb)
- Paweł Szczepanek (pauluz)
- Christian López Espínola (penyaskito)
- Petr Jaroš (petajaros)
- Philipp Hoffmann (philipphoffmann)
- Alex Carol (picard89)

View File

@ -1,393 +1,313 @@
{# Widgets #}
{% block form_widget %}
{% spaceless %}
{% block form_widget -%}
{% if compound %}
{{ block('form_widget_compound') }}
{{- block('form_widget_compound') -}}
{% else %}
{{ block('form_widget_simple') }}
{{- block('form_widget_simple') -}}
{% endif %}
{% endspaceless %}
{% endblock form_widget %}
{%- endblock form_widget %}
{% block form_widget_simple %}
{% spaceless %}
{% set type = type|default('text') %}
{% block form_widget_simple -%}
{%- set type = type|default('text') -%}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endspaceless %}
{% endblock form_widget_simple %}
{%- endblock form_widget_simple %}
{% block form_widget_compound %}
{% spaceless %}
{% block form_widget_compound -%}
<div {{ block('widget_container_attributes') }}>
{% if form.parent is empty %}
{%- if form.parent is empty -%}
{{ form_errors(form) }}
{% endif %}
{{ block('form_rows') }}
{{ form_rest(form) }}
{%- endif -%}
{{- block('form_rows') -}}
{{- form_rest(form) -}}
</div>
{% endspaceless %}
{% endblock form_widget_compound %}
{%- endblock form_widget_compound %}
{% block collection_widget %}
{% spaceless %}
{% block collection_widget -%}
{% if prototype is defined %}
{% set attr = attr|merge({'data-prototype': form_row(prototype) }) %}
{%- set attr = attr|merge({'data-prototype': form_row(prototype) }) -%}
{% endif %}
{{ block('form_widget') }}
{% endspaceless %}
{% endblock collection_widget %}
{{- block('form_widget') -}}
{%- endblock collection_widget %}
{% block textarea_widget %}
{% spaceless %}
{% block textarea_widget -%}
<textarea {{ block('widget_attributes') }}>{{ value }}</textarea>
{% endspaceless %}
{% endblock textarea_widget %}
{%- endblock textarea_widget %}
{% block choice_widget %}
{% spaceless %}
{% block choice_widget -%}
{% if expanded %}
{{ block('choice_widget_expanded') }}
{{- block('choice_widget_expanded') -}}
{% else %}
{{ block('choice_widget_collapsed') }}
{{- block('choice_widget_collapsed') -}}
{% endif %}
{% endspaceless %}
{% endblock choice_widget %}
{%- endblock choice_widget %}
{% block choice_widget_expanded %}
{% spaceless %}
{% block choice_widget_expanded -%}
<div {{ block('widget_container_attributes') }}>
{% for child in form %}
{{ form_widget(child) }}
{{ form_label(child) }}
{{- form_widget(child) -}}
{{- form_label(child) -}}
{% endfor %}
</div>
{% endspaceless %}
{% endblock choice_widget_expanded %}
{%- endblock choice_widget_expanded %}
{% block choice_widget_collapsed %}
{% spaceless %}
{% block choice_widget_collapsed -%}
{% if required and empty_value is none and not empty_value_in_choices and not multiple %}
{% set required = false %}
{% endif %}
<select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{% if empty_value is not none %}
{% if empty_value is not none -%}
<option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ empty_value|trans({}, translation_domain) }}</option>
{% endif %}
{% if preferred_choices|length > 0 %}
{%- endif %}
{%- if preferred_choices|length > 0 -%}
{% set options = preferred_choices %}
{{ block('choice_widget_options') }}
{% if choices|length > 0 and separator is not none %}
{{- block('choice_widget_options') -}}
{% if choices|length > 0 and separator is not none -%}
<option disabled="disabled">{{ separator }}</option>
{% endif %}
{% endif %}
{% set options = choices %}
{{ block('choice_widget_options') }}
{%- endif %}
{%- endif -%}
{% set options = choices -%}
{{- block('choice_widget_options') -}}
</select>
{% endspaceless %}
{% endblock choice_widget_collapsed %}
{%- endblock choice_widget_collapsed %}
{% block choice_widget_options %}
{% spaceless %}
{% block choice_widget_options -%}
{% for group_label, choice in options %}
{% if choice is iterable %}
{%- if choice is iterable -%}
<optgroup label="{{ group_label|trans({}, translation_domain) }}">
{% set options = choice %}
{{ block('choice_widget_options') }}
{{- block('choice_widget_options') -}}
</optgroup>
{% else %}
{%- else -%}
<option value="{{ choice.value }}"{% if choice is selectedchoice(value) %} selected="selected"{% endif %}>{{ choice.label|trans({}, translation_domain) }}</option>
{% endif %}
{%- endif -%}
{% endfor %}
{% endspaceless %}
{% endblock choice_widget_options %}
{%- endblock choice_widget_options %}
{% block checkbox_widget %}
{% spaceless %}
{% block checkbox_widget -%}
<input type="checkbox" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
{% endspaceless %}
{% endblock checkbox_widget %}
{%- endblock checkbox_widget %}
{% block radio_widget %}
{% spaceless %}
{% block radio_widget -%}
<input type="radio" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
{% endspaceless %}
{% endblock radio_widget %}
{%- endblock radio_widget %}
{% block datetime_widget %}
{% spaceless %}
{% block datetime_widget -%}
{% if widget == 'single_text' %}
{{ block('form_widget_simple') }}
{% else %}
{{- block('form_widget_simple') -}}
{% else -%}
<div {{ block('widget_container_attributes') }}>
{{ form_errors(form.date) }}
{{ form_errors(form.time) }}
{{ form_widget(form.date) }}
{{ form_widget(form.time) }}
{{- form_errors(form.date) -}}
{{- form_errors(form.time) -}}
{{- form_widget(form.date) -}}
{{- form_widget(form.time) -}}
</div>
{% endif %}
{% endspaceless %}
{% endblock datetime_widget %}
{%- endif %}
{%- endblock datetime_widget %}
{% block date_widget %}
{% spaceless %}
{% if widget == 'single_text' %}
{% block date_widget -%}
{% if widget == 'single_text' -%}
{{ block('form_widget_simple') }}
{% else %}
{%- else -%}
<div {{ block('widget_container_attributes') }}>
{{ date_pattern|replace({
{{- date_pattern|replace({
'{{ year }}': form_widget(form.year),
'{{ month }}': form_widget(form.month),
'{{ day }}': form_widget(form.day),
})|raw }}
})|raw -}}
</div>
{% endif %}
{% endspaceless %}
{% endblock date_widget %}
{%- endif %}
{%- endblock date_widget %}
{% block time_widget %}
{% spaceless %}
{% if widget == 'single_text' %}
{% block time_widget -%}
{% if widget == 'single_text' -%}
{{ block('form_widget_simple') }}
{% else %}
{% set vars = widget == 'text' ? { 'attr': { 'size': 1 }} : {} %}
{%- else -%}
{% set vars = widget == 'text' ? { 'attr': { 'size': 1 }} : {} -%}
<div {{ block('widget_container_attributes') }}>
{{ form_widget(form.hour, vars) }}{% if with_minutes %}:{{ form_widget(form.minute, vars) }}{% endif %}{% if with_seconds %}:{{ form_widget(form.second, vars) }}{% endif %}
</div>
{% endif %}
{% endspaceless %}
{% endblock time_widget %}
{%- endif %}
{%- endblock time_widget %}
{% block number_widget %}
{% spaceless %}
{% block number_widget -%}
{# type="number" doesn't work with floats #}
{% set type = type|default('text') %}
{%- set type = type|default('text') -%}
{{ block('form_widget_simple') }}
{% endspaceless %}
{% endblock number_widget %}
{%- endblock number_widget %}
{% block integer_widget %}
{% spaceless %}
{% block integer_widget -%}
{% set type = type|default('number') %}
{{ block('form_widget_simple') }}
{% endspaceless %}
{% endblock integer_widget %}
{{- block('form_widget_simple') -}}
{%- endblock integer_widget %}
{% block money_widget %}
{% spaceless %}
{% block money_widget -%}
{{ money_pattern|replace({ '{{ widget }}': block('form_widget_simple') })|raw }}
{% endspaceless %}
{% endblock money_widget %}
{%- endblock money_widget %}
{% block url_widget %}
{% spaceless %}
{% set type = type|default('url') %}
{% block url_widget -%}
{% set type = type|default('url') -%}
{{ block('form_widget_simple') }}
{% endspaceless %}
{% endblock url_widget %}
{%- endblock url_widget %}
{% block search_widget %}
{% spaceless %}
{% set type = type|default('search') %}
{% block search_widget -%}
{% set type = type|default('search') -%}
{{ block('form_widget_simple') }}
{% endspaceless %}
{% endblock search_widget %}
{%- endblock search_widget %}
{% block percent_widget %}
{% spaceless %}
{% set type = type|default('text') %}
{% block percent_widget -%}
{% set type = type|default('text') -%}
{{ block('form_widget_simple') }} %
{% endspaceless %}
{% endblock percent_widget %}
{%- endblock percent_widget %}
{% block password_widget %}
{% spaceless %}
{% set type = type|default('password') %}
{% block password_widget -%}
{% set type = type|default('password') -%}
{{ block('form_widget_simple') }}
{% endspaceless %}
{% endblock password_widget %}
{%- endblock password_widget %}
{% block hidden_widget %}
{% spaceless %}
{% set type = type|default('hidden') %}
{% block hidden_widget -%}
{% set type = type|default('hidden') -%}
{{ block('form_widget_simple') }}
{% endspaceless %}
{% endblock hidden_widget %}
{%- endblock hidden_widget %}
{% block email_widget %}
{% spaceless %}
{% set type = type|default('email') %}
{% block email_widget -%}
{% set type = type|default('email') -%}
{{ block('form_widget_simple') }}
{% endspaceless %}
{% endblock email_widget %}
{%- endblock email_widget %}
{% block button_widget %}
{% spaceless %}
{% if label is empty %}
{% block button_widget -%}
{% if label is empty -%}
{% set label = name|humanize %}
{% endif %}
{%- endif -%}
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>{{ label|trans({}, translation_domain) }}</button>
{% endspaceless %}
{% endblock button_widget %}
{%- endblock button_widget %}
{% block submit_widget %}
{% spaceless %}
{% set type = type|default('submit') %}
{% block submit_widget -%}
{% set type = type|default('submit') -%}
{{ block('button_widget') }}
{% endspaceless %}
{% endblock submit_widget %}
{%- endblock submit_widget %}
{% block reset_widget %}
{% spaceless %}
{% set type = type|default('reset') %}
{% block reset_widget -%}
{% set type = type|default('reset') -%}
{{ block('button_widget') }}
{% endspaceless %}
{% endblock reset_widget %}
{%- endblock reset_widget %}
{# Labels #}
{% block form_label %}
{% spaceless %}
{% block form_label -%}
{% if label is not sameas(false) %}
{% if not compound %}
{%- if not compound -%}
{% set label_attr = label_attr|merge({'for': id}) %}
{% endif %}
{% if required %}
{%- endif -%}
{%- if required -%}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
{% endif %}
{% if label is empty %}
{%- endif -%}
{%- if label is empty -%}
{% set label = name|humanize %}
{% endif %}
{%- endif -%}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans({}, translation_domain) }}</label>
{% endif %}
{% endspaceless %}
{% endblock form_label %}
{%- endif %}
{%- endblock form_label %}
{% block button_label %}{% endblock %}
{% block button_label -%}{%- endblock %}
{# Rows #}
{% block repeated_row %}
{% spaceless %}
{% block repeated_row -%}
{#
No need to render the errors here, as all errors are mapped
to the first child (see RepeatedTypeValidatorExtension).
#}
{{ block('form_rows') }}
{% endspaceless %}
{% endblock repeated_row %}
{{- block('form_rows') }}
{%- endblock repeated_row %}
{% block form_row %}
{% spaceless %}
{% block form_row -%}
<div>
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
{{- form_label(form) -}}
{{- form_errors(form) -}}
{{- form_widget(form) -}}
</div>
{% endspaceless %}
{% endblock form_row %}
{%- endblock form_row %}
{% block button_row %}
{% spaceless %}
{% block button_row -%}
<div>
{{ form_widget(form) }}
{{- form_widget(form) -}}
</div>
{% endspaceless %}
{% endblock button_row %}
{%- endblock button_row %}
{% block hidden_row %}
{% block hidden_row -%}
{{ form_widget(form) }}
{% endblock hidden_row %}
{%- endblock hidden_row %}
{# Misc #}
{% block form %}
{% spaceless %}
{% block form -%}
{{ form_start(form) }}
{{ form_widget(form) }}
{{- form_widget(form) -}}
{{ form_end(form) }}
{% endspaceless %}
{% endblock form %}
{%- endblock form %}
{% block form_start %}
{% spaceless %}
{% block form_start -%}
{% set method = method|upper %}
{% if method in ["GET", "POST"] %}
{%- if method in ["GET", "POST"] -%}
{% set form_method = method %}
{% else %}
{%- else -%}
{% set form_method = "POST" %}
{% endif %}
{%- endif -%}
<form method="{{ form_method|lower }}" action="{{ action }}"{% for attrname, attrvalue in attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}{% if multipart %} enctype="multipart/form-data"{% endif %}>
{% if form_method != method %}
{%- if form_method != method -%}
<input type="hidden" name="_method" value="{{ method }}" />
{% endif %}
{% endspaceless %}
{% endblock form_start %}
{%- endif %}
{%- endblock form_start %}
{% block form_end %}
{% spaceless %}
{% if not render_rest is defined or render_rest %}
{% block form_end -%}
{% if not render_rest is defined or render_rest -%}
{{ form_rest(form) }}
{% endif %}
{%- endif -%}
</form>
{% endspaceless %}
{% endblock form_end %}
{%- endblock form_end %}
{% block form_enctype %}
{% spaceless %}
{% block form_enctype -%}
{% if multipart %}enctype="multipart/form-data"{% endif %}
{% endspaceless %}
{% endblock form_enctype %}
{%- endblock form_enctype %}
{% block form_errors %}
{% spaceless %}
{% if errors|length > 0 %}
{% block form_errors -%}
{% if errors|length > 0 -%}
<ul>
{% for error in errors %}
{%- for error in errors -%}
<li>{{ error.message }}</li>
{% endfor %}
{%- endfor -%}
</ul>
{% endif %}
{% endspaceless %}
{% endblock form_errors %}
{%- endif %}
{%- endblock form_errors %}
{% block form_rest %}
{% spaceless %}
{% for child in form %}
{% if not child.rendered %}
{% block form_rest -%}
{% for child in form -%}
{% if not child.rendered -%}
{{ form_row(child) }}
{% endif %}
{% endfor %}
{% endspaceless %}
{% endblock form_rest %}
{%- endif %}
{%- endfor %}
{%- endblock form_rest %}
{# Support #}
{% block form_rows %}
{% spaceless %}
{% for child in form %}
{% block form_rows -%}
{% for child in form -%}
{{ form_row(child) }}
{% endfor %}
{% endspaceless %}
{% endblock form_rows %}
{%- endfor %}
{%- endblock form_rows %}
{% block widget_attributes %}
{% spaceless %}
{% block widget_attributes -%}
id="{{ id }}" name="{{ full_name }}"{% if read_only %} readonly="readonly"{% endif %}{% if disabled %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
{% for attrname, attrvalue in attr %}{% if attrname in ['placeholder', 'title'] %}{{ attrname }}="{{ attrvalue|trans({}, translation_domain) }}" {% else %}{{ attrname }}="{{ attrvalue }}" {% endif %}{% endfor %}
{% endspaceless %}
{% endblock widget_attributes %}
{%- endblock widget_attributes %}
{% block widget_container_attributes %}
{% spaceless %}
{% block widget_container_attributes -%}
{% if id is not empty %}id="{{ id }}" {% endif %}
{% for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}
{% endspaceless %}
{% endblock widget_container_attributes %}
{%- for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}
{%- endblock widget_container_attributes %}
{% block button_attributes %}
{% spaceless %}
{% block button_attributes -%}
id="{{ id }}" name="{{ full_name }}"{% if disabled %} disabled="disabled"{% endif %}
{% for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}
{% endspaceless %}
{% endblock button_attributes %}
{%- for attrname, attrvalue in attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}
{%- endblock button_attributes %}

View File

@ -1,52 +1,44 @@
{% use "form_div_layout.html.twig" %}
{% block form_row %}
{% spaceless %}
{% block form_row -%}
<tr>
<td>
{{ form_label(form) }}
{{- form_label(form) -}}
</td>
<td>
{{ form_errors(form) }}
{{ form_widget(form) }}
{{- form_errors(form) -}}
{{- form_widget(form) -}}
</td>
</tr>
{% endspaceless %}
{% endblock form_row %}
{%- endblock form_row %}
{% block button_row %}
{% spaceless %}
{% block button_row -%}
<tr>
<td></td>
<td>
{{ form_widget(form) }}
{{- form_widget(form) -}}
</td>
</tr>
{% endspaceless %}
{% endblock button_row %}
{%- endblock button_row %}
{% block hidden_row %}
{% spaceless %}
{% block hidden_row -%}
<tr style="display: none">
<td colspan="2">
{{ form_widget(form) }}
{{- form_widget(form) -}}
</td>
</tr>
{% endspaceless %}
{% endblock hidden_row %}
{%- endblock hidden_row %}
{% block form_widget_compound %}
{% spaceless %}
{% block form_widget_compound -%}
<table {{ block('widget_container_attributes') }}>
{% if form.parent is empty and errors|length > 0 %}
{% if form.parent is empty and errors|length > 0 -%}
<tr>
<td colspan="2">
{{ form_errors(form) }}
{{- form_errors(form) -}}
</td>
</tr>
{% endif %}
{{ block('form_rows') }}
{{ form_rest(form) }}
{%- endif %}
{{- block('form_rows') -}}
{{- form_rest(form) -}}
</table>
{% endspaceless %}
{% endblock form_widget_compound %}
{%- endblock form_widget_compound %}

View File

@ -45,7 +45,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
{
$translator = $this->getTranslator($this->getLoader());
$translator->setLocale('fr');
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR'));
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
$this->assertEquals('foo (FR)', $translator->trans('foo'));
$this->assertEquals('bar (EN)', $translator->trans('bar'));
@ -54,6 +54,8 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('no translation', $translator->trans('no translation'));
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
}
public function testTransWithCaching()
@ -61,7 +63,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
// prime the cache
$translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir));
$translator->setLocale('fr');
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR'));
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
$this->assertEquals('foo (FR)', $translator->trans('foo'));
$this->assertEquals('bar (EN)', $translator->trans('bar'));
@ -70,12 +72,14 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('no translation', $translator->trans('no translation'));
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
// do it another time as the cache is primed now
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
$translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir));
$translator->setLocale('fr');
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR'));
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
$this->assertEquals('foo (FR)', $translator->trans('foo'));
$this->assertEquals('bar (EN)', $translator->trans('bar'));
@ -84,6 +88,8 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('no translation', $translator->trans('no translation'));
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
}
public function testGetLocale()
@ -175,6 +181,20 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
'other choice' => '{0} other choice 0 (PT-BR)|{1} other choice 1 (PT-BR)|]1,Inf] other choice inf (PT-BR)',
))))
;
$loader
->expects($this->at(5))
->method('load')
->will($this->returnValue($this->getCatalogue('fr.UTF-8', array(
'foobarbaz' => 'foobarbaz (fr.UTF-8)',
))))
;
$loader
->expects($this->at(6))
->method('load')
->will($this->returnValue($this->getCatalogue('sr@latin', array(
'foobarbax' => 'foobarbax (sr@latin)',
))))
;
return $loader;
}
@ -205,6 +225,8 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$translator->addResource('loader', 'foo', 'es');
$translator->addResource('loader', 'foo', 'pt-PT'); // European Portuguese
$translator->addResource('loader', 'foo', 'pt_BR'); // Brazilian Portuguese
$translator->addResource('loader', 'foo', 'fr.UTF-8');
$translator->addResource('loader', 'foo', 'sr@latin'); // Latin Serbian
return $translator;
}

View File

@ -97,8 +97,10 @@ class Translator extends BaseTranslator
$fallbackContent = '';
$current = '';
$replacementPattern = '/[^a-z0-9_]/i';
foreach ($this->computeFallbackLocales($locale) as $fallback) {
$fallbackSuffix = ucfirst(str_replace('-', '_', $fallback));
$fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback));
$currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current));
$fallbackContent .= sprintf(<<<EOF
\$catalogue%s = new MessageCatalogue('%s', %s);
@ -110,7 +112,7 @@ EOF
$fallbackSuffix,
$fallback,
var_export($this->catalogues[$fallback]->all(), true),
ucfirst(str_replace('-', '_', $current)),
$currentSuffix,
$fallbackSuffix
);
$current = $fallback;

View File

@ -304,7 +304,7 @@ abstract class Client
$uri = $this->getAbsoluteUri($uri);
if (isset($server['HTTP_HOST'])) {
$uri = preg_replace('{^(https?\://)'.parse_url($uri, PHP_URL_HOST).'}', '${1}'.$server['HTTP_HOST'], $uri);
$uri = preg_replace('{^(https?\://)'.preg_quote($this->extractHost($uri)).'}', '${1}'.$server['HTTP_HOST'], $uri);
}
if (isset($server['HTTPS'])) {
@ -317,12 +317,7 @@ abstract class Client
$server['HTTP_REFERER'] = $this->history->current()->getUri();
}
$server['HTTP_HOST'] = parse_url($uri, PHP_URL_HOST);
if ($port = parse_url($uri, PHP_URL_PORT)) {
$server['HTTP_HOST'] .= ':'.$port;
}
$server['HTTP_HOST'] = $this->extractHost($uri);
$server['HTTPS'] = 'https' == parse_url($uri, PHP_URL_SCHEME);
$this->internalRequest = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content);
@ -619,4 +614,15 @@ abstract class Client
return $server;
}
private function extractHost($uri)
{
$host = parse_url($uri, PHP_URL_HOST);
if ($port = parse_url($uri, PHP_URL_PORT)) {
return $host.':'.$port;
}
return $host;
}
}

View File

@ -210,6 +210,24 @@ class ClientTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('http://www.example.com/foo/bar', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs');
}
public function testRequestURIConversionByServerHost()
{
$client = new TestClient();
$server = array('HTTP_HOST' => 'www.exampl+e.com:8000');
$parameters = array();
$files = array();
$client->request('GET', 'http://exampl+e.com', $parameters, $files, $server);
$this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST to add port');
$client->request('GET', 'http://exampl+e.com:8888', $parameters, $files, $server);
$this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST to modify existing port');
$client->request('GET', 'http://exampl+e.com:8000', $parameters, $files, $server);
$this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST respects correct set port');
}
public function testRequestReferer()
{
$client = new TestClient();

View File

@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Scope;
/**
* GraphvizDumper dumps a service container as a graphviz file.
@ -193,8 +194,8 @@ class GraphvizDumper extends Dumper
$container->setDefinitions($this->container->getDefinitions());
$container->setAliases($this->container->getAliases());
$container->setResources($this->container->getResources());
foreach ($this->container->getScopes() as $scope) {
$container->addScope($scope);
foreach ($this->container->getScopes() as $scope => $parentScope) {
$container->addScope(new Scope($scope, $parentScope));
}
foreach ($this->container->getExtensions() as $extension) {
$container->registerExtension($extension);

View File

@ -62,4 +62,11 @@ class GraphvizDumperTest extends \PHPUnit_Framework_TestCase
$dumper = new GraphvizDumper($container);
$this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services14.dot')), $dumper->dump(), '->dump() dumps services');
}
public function testDumpWithScopes()
{
$container = include self::$fixturesPath.'/containers/container18.php';
$dumper = new GraphvizDumper($container);
$this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services18.dot')), $dumper->dump(), '->dump() dumps services');
}
}

View File

@ -0,0 +1,14 @@
<?php
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Scope;
$container = new ContainerBuilder();
$container->addScope(new Scope('request'));
$container->
register('foo', 'FooClass')->
setScope('request')
;
$container->compile();
return $container;

View File

@ -0,0 +1,8 @@
digraph sc {
ratio="compress"
node [fontsize="11" fontname="Arial" shape="record"];
edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"];
node_foo [label="foo\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"];
}

View File

@ -152,6 +152,8 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
* Returns a preconfigured IntlDateFormatter instance
*
* @return \IntlDateFormatter
*
* @throws TransformationFailedException in case the date formatter can not be constructed.
*/
protected function getIntlDateFormatter()
{
@ -162,6 +164,12 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
$pattern = $this->pattern;
$intlDateFormatter = new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone, $calendar, $pattern);
// new \intlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/bug.php?id=66323
if (!$intlDateFormatter) {
throw new TransformationFailedException(intl_get_error_message(), intl_get_error_code());
}
$intlDateFormatter->setLenient(false);
return $intlDateFormatter;

View File

@ -77,6 +77,12 @@ class DateType extends AbstractType
$calendar,
$pattern
);
// new \intlDateFormatter may return null instead of false in case of failure, see https://bugs.php.net/bug.php?id=66323
if (!$formatter) {
throw new InvalidOptionsException(intl_get_error_message(), intl_get_error_code());
}
$formatter->setLenient(false);
if ('choice' === $options['widget']) {

View File

@ -111,7 +111,7 @@ class JsonResponse extends Response
// Not using application/javascript for compatibility reasons with older browsers.
$this->headers->set('Content-Type', 'text/javascript');
return $this->setContent(sprintf('%s(%s);', $this->callback, $this->data));
return $this->setContent(sprintf('/**/%s(%s);', $this->callback, $this->data));
}
// Only set the header when there is none or when it equals 'text/javascript' (from a previous update with callback)

View File

@ -155,7 +155,7 @@ class JsonResponseTest extends \PHPUnit_Framework_TestCase
{
$response = JsonResponse::create(array('foo' => 'bar'))->setCallback('callback');
$this->assertEquals('callback({"foo":"bar"});', $response->getContent());
$this->assertEquals('/**/callback({"foo":"bar"});', $response->getContent());
$this->assertEquals('text/javascript', $response->headers->get('Content-Type'));
}

View File

@ -59,11 +59,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $startTime;
protected $loadClassCache;
const VERSION = '2.3.18-DEV';
const VERSION_ID = '20318';
const VERSION = '2.3.19-DEV';
const VERSION_ID = '20319';
const MAJOR_VERSION = '2';
const MINOR_VERSION = '3';
const RELEASE_VERSION = '18';
const RELEASE_VERSION = '19';
const EXTRA_VERSION = 'DEV';
/**

View File

@ -59,8 +59,7 @@ class ExecutableFinder
if (is_dir($path)) {
$dirs[] = $path;
} else {
$file = str_replace(dirname($path), '', $path);
if ($file == $name && is_executable($path)) {
if (basename($path) == $name && is_executable($path)) {
return $path;
}
}

View File

@ -64,6 +64,8 @@ class Process
/** @var ProcessPipes */
private $processPipes;
private $latestSignal;
private static $sigchild;
/**
@ -321,7 +323,7 @@ class Process
usleep(1000);
}
if ($this->processInformation['signaled']) {
if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) {
throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
}
@ -661,7 +663,8 @@ class Process
throw new RuntimeException('Unable to kill the process');
}
}
proc_terminate($this->process);
// given `SIGTERM` may not be defined and that `proc_terminate` uses the constant value and not the constant itself, we use the same here
$this->doSignal(15, false);
do {
usleep(1000);
} while ($this->isRunning() && microtime(true) < $timeoutMicro);
@ -1158,6 +1161,7 @@ class Process
$this->stdout = null;
$this->stderr = null;
$this->process = null;
$this->latestSignal = null;
$this->status = self::STATUS_READY;
$this->incrementalOutputOffset = 0;
$this->incrementalErrorOutputOffset = 0;
@ -1201,6 +1205,8 @@ class Process
return false;
}
$this->latestSignal = $signal;
return true;
}

View File

@ -284,6 +284,8 @@ class ProcessPipes
private function readStreams($blocking, $close = false)
{
if (empty($this->pipes)) {
usleep(Process::TIMEOUT_PRECISION * 1E4);
return array();
}
@ -313,11 +315,11 @@ class ProcessPipes
$type = array_search($pipe, $this->pipes);
$data = '';
while ($dataread = fread($pipe, self::CHUNK_SIZE)) {
while ('' !== $dataread = (string) fread($pipe, self::CHUNK_SIZE)) {
$data .= $dataread;
}
if ($data) {
if ('' !== $data) {
$read[$type] = $data;
}

View File

@ -287,6 +287,19 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
}
}
public function testZeroAsOutput()
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
// see http://stackoverflow.com/questions/7105433/windows-batch-echo-without-new-line
$p = $this->getProcess('echo | set /p dummyName=0');
} else {
$p = $this->getProcess('printf 0');
}
$p->run();
$this->assertSame('0', $p->getOutput());
}
public function testExitCodeCommandFailed()
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
@ -583,7 +596,14 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
}
$duration = microtime(true) - $start;
$this->assertLessThan($timeout + Process::TIMEOUT_PRECISION, $duration);
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
// Windows is a bit slower as it read file handles, then allow twice the precision
$maxDuration = $timeout + 2 * Process::TIMEOUT_PRECISION;
} else {
$maxDuration = $timeout + Process::TIMEOUT_PRECISION;
}
$this->assertLessThan($maxDuration, $duration);
}
public function testCheckTimeoutOnNonStartedProcess()

View File

@ -47,9 +47,9 @@ class ExecutableFinderTest extends \PHPUnit_Framework_TestCase
$this->setPath(dirname(PHP_BINARY));
$finder = new ExecutableFinder;
$result = $finder->find(basename(PHP_BINARY));
$result = $finder->find($this->getPhpBinaryName());
$this->assertEquals($result, PHP_BINARY);
$this->assertSamePath(PHP_BINARY, $result);
}
public function testFindWithDefault()
@ -83,9 +83,9 @@ class ExecutableFinderTest extends \PHPUnit_Framework_TestCase
$extraDirs = array(dirname(PHP_BINARY));
$finder = new ExecutableFinder;
$result = $finder->find(basename(PHP_BINARY), null, $extraDirs);
$result = $finder->find($this->getPhpBinaryName(), null, $extraDirs);
$this->assertEquals(PHP_BINARY, $result);
$this->assertSamePath(PHP_BINARY, $result);
}
public function testFindWithOpenBaseDir()
@ -105,8 +105,43 @@ class ExecutableFinderTest extends \PHPUnit_Framework_TestCase
ini_set('open_basedir', dirname(PHP_BINARY).PATH_SEPARATOR.'/');
$finder = new ExecutableFinder;
$result = $finder->find(basename(PHP_BINARY));
$result = $finder->find($this->getPhpBinaryName());
$this->assertEquals(PHP_BINARY, $result);
$this->assertSamePath(PHP_BINARY, $result);
}
public function testFindProcessInOpenBasedir()
{
if (ini_get('open_basedir')) {
$this->markTestSkipped('Cannot test when open_basedir is set');
}
if (!defined('PHP_BINARY')) {
$this->markTestSkipped('Requires the PHP_BINARY constant');
}
$execPath = __DIR__.'/SignalListener.php';
$this->setPath('');
ini_set('open_basedir', PHP_BINARY.PATH_SEPARATOR.'/');
$finder = new ExecutableFinder();
$result = $finder->find($this->getPhpBinaryName(), false);
$this->assertSamePath(PHP_BINARY, $result);
}
private function assertSamePath($expected, $tested)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->assertEquals(strtolower($expected), strtolower($tested));
} else {
$this->assertEquals($expected, $tested);
}
}
private function getPhpBinaryName()
{
return basename(PHP_BINARY, defined('PHP_WINDOWS_VERSION_BUILD') ? '.exe' : '');
}
}

View File

@ -211,6 +211,11 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
$this->markTestSkipped('Signal is not supported in sigchild environment');
}
public function testRunProcessWithTimeout()
{
$this->markTestSkipped('Signal (required for timeout) is not supported in sigchild environment');
}
/**
* {@inheritdoc}
*/

View File

@ -120,6 +120,21 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
parent::testStartAfterATimeout();
}
public function testStopWithTimeoutIsActuallyWorking()
{
$this->markTestSkipped('Stopping with signal is not supported in sigchild environment');
}
public function testRunProcessWithTimeout()
{
$this->markTestSkipped('Signal (required for timeout) is not supported in sigchild environment');
}
public function testCheckTimeoutOnStartedProcess()
{
$this->markTestSkipped('Signal (required for timeout) is not supported in sigchild environment');
}
/**
* {@inheritdoc}
*/

View File

@ -147,6 +147,57 @@ class SimpleProcessTest extends AbstractProcessTest
parent::testSignalWithWrongNonIntSignal();
}
public function testStopTerminatesProcessCleanly()
{
try {
$process = $this->getProcess('php -r "echo \'foo\'; sleep(1); echo \'bar\';"');
$process->run(function () use ($process) {
$process->stop();
});
} catch (RuntimeException $e) {
$this->fail('A call to stop() is not expected to cause wait() to throw a RuntimeException');
}
}
public function testKillSignalTerminatesProcessCleanly()
{
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
try {
$process = $this->getProcess('php -r "echo \'foo\'; sleep(1); echo \'bar\';"');
$process->run(function () use ($process) {
if ($process->isRunning()) {
$process->signal(SIGKILL);
}
});
} catch (RuntimeException $e) {
$this->fail('A call to signal() is not expected to cause wait() to throw a RuntimeException');
}
}
public function testTermSignalTerminatesProcessCleanly()
{
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
try {
$process = $this->getProcess('php -r "echo \'foo\'; sleep(1); echo \'bar\';"');
$process->run(function () use ($process) {
if ($process->isRunning()) {
$process->signal(SIGTERM);
}
});
} catch (RuntimeException $e) {
$this->fail('A call to signal() is not expected to cause wait() to throw a RuntimeException');
}
}
public function testStopWithTimeoutIsActuallyWorking()
{
$this->skipIfPHPSigchild();
parent::testStopWithTimeoutIsActuallyWorking();
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,33 @@ use Symfony\Component\Translation\Loader\ArrayLoader;
class TranslatorTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
*/
public function testConstructorInvalidLocale($locale)
{
$translator = new Translator($locale, new MessageSelector());
}
/**
* @dataProvider getValidLocalesTests
*/
public function testConstructorValidLocale($locale)
{
$translator = new Translator($locale, new MessageSelector());
$this->assertEquals($locale, $translator->getLocale());
}
public function testConstructorWithoutLocale()
{
$translator = new Translator(null, new MessageSelector());
$this->assertNull($translator->getLocale());
}
public function testSetGetLocale()
{
$translator = new Translator('en', new MessageSelector());
@ -27,6 +54,27 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('fr', $translator->getLocale());
}
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
*/
public function testSetInvalidLocale($locale)
{
$translator = new Translator('fr', new MessageSelector());
$translator->setLocale($locale);
}
/**
* @dataProvider getValidLocalesTests
*/
public function testSetValidLocale($locale)
{
$translator = new Translator($locale, new MessageSelector());
$translator->setLocale($locale);
$this->assertEquals($locale, $translator->getLocale());
}
public function testSetFallbackLocales()
{
$translator = new Translator('en', new MessageSelector());
@ -55,6 +103,26 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('bar (fr)', $translator->trans('bar'));
}
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
*/
public function testSetFallbackInvalidLocales($locale)
{
$translator = new Translator('fr', new MessageSelector());
$translator->setFallbackLocales(array('fr', $locale));
}
/**
* @dataProvider getValidLocalesTests
*/
public function testSetFallbackValidLocales($locale)
{
$translator = new Translator($locale, new MessageSelector());
$translator->setFallbackLocales(array('fr', $locale));
// no assertion. this method just asserts that no exception is thrown
}
public function testTransWithFallbackLocale()
{
$translator = new Translator('fr_FR', new MessageSelector());
@ -67,6 +135,26 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('foobar', $translator->trans('bar'));
}
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
*/
public function testAddResourceInvalidLocales($locale)
{
$translator = new Translator('fr', new MessageSelector());
$translator->addResource('array', array('foo' => 'foofoo'), $locale);
}
/**
* @dataProvider getValidLocalesTests
*/
public function testAddResourceValidLocales($locale)
{
$translator = new Translator('fr', new MessageSelector());
$translator->addResource('array', array('foo' => 'foofoo'), $locale);
// no assertion. this method just asserts that no exception is thrown
}
public function testAddResourceAfterTrans()
{
$translator = new Translator('fr', new MessageSelector());
@ -164,6 +252,32 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, $translator->trans($id, $parameters, $domain, $locale));
}
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
*/
public function testTransInvalidLocale($locale)
{
$translator = new Translator('en', new MessageSelector());
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foofoo'), 'en');
$translator->trans('foo', array(), '', $locale);
}
/**
* @dataProvider getValidLocalesTests
*/
public function testTransValidLocale($locale)
{
$translator = new Translator('en', new MessageSelector());
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foofoo'), 'en');
$translator->trans('foo', array(), '', $locale);
// no assertion. this method just asserts that no exception is thrown
}
/**
* @dataProvider getFlattenedTransTests
*/
@ -188,6 +302,32 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, $translator->transChoice($id, $number, $parameters, $domain, $locale));
}
/**
* @dataProvider getInvalidLocalesTests
* @expectedException \InvalidArgumentException
*/
public function testTransChoiceInvalidLocale($locale)
{
$translator = new Translator('en', new MessageSelector());
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foofoo'), 'en');
$translator->transChoice('foo', 1, array(), '', $locale);
}
/**
* @dataProvider getValidLocalesTests
*/
public function testTransChoiceValidLocale($locale)
{
$translator = new Translator('en', new MessageSelector());
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foofoo'), 'en');
$translator->transChoice('foo', 1, array(), '', $locale);
// no assertion. this method just asserts that no exception is thrown
}
public function getTransFileTests()
{
return array(
@ -257,6 +397,40 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
);
}
public function getInvalidLocalesTests()
{
return array(
array('fr FR'),
array('français'),
array('fr+en'),
array('utf#8'),
array('fr&en'),
array('fr~FR'),
array(' fr'),
array('fr '),
array('fr*'),
array('fr/FR'),
array('fr\\FR'),
);
}
public function getValidLocalesTests()
{
return array(
array(''),
array(null),
array('fr'),
array('francais'),
array('FR'),
array('frFR'),
array('fr-FR'),
array('fr_FR'),
array('fr.FR'),
array('fr-FR.UTF8'),
array('sr@latin'),
);
}
public function testTransChoiceFallback()
{
$translator = new Translator('ru', new MessageSelector());

View File

@ -59,11 +59,13 @@ class Translator implements TranslatorInterface
* @param string $locale The locale
* @param MessageSelector|null $selector The message selector for pluralization
*
* @throws \InvalidArgumentException If a locale contains invalid characters
*
* @api
*/
public function __construct($locale, MessageSelector $selector = null)
{
$this->locale = $locale;
$this->setLocale($locale);
$this->selector = $selector ?: new MessageSelector();
}
@ -88,6 +90,8 @@ class Translator implements TranslatorInterface
* @param string $locale The locale
* @param string $domain The domain
*
* @throws \InvalidArgumentException If the locale contains invalid characters
*
* @api
*/
public function addResource($format, $resource, $locale, $domain = null)
@ -96,6 +100,8 @@ class Translator implements TranslatorInterface
$domain = 'messages';
}
$this->assertValidLocale($locale);
$this->resources[$locale][] = array($format, $resource, $domain);
if (in_array($locale, $this->fallbackLocales)) {
@ -112,6 +118,7 @@ class Translator implements TranslatorInterface
*/
public function setLocale($locale)
{
$this->assertValidLocale($locale);
$this->locale = $locale;
}
@ -130,6 +137,8 @@ class Translator implements TranslatorInterface
*
* @param string|array $locales The fallback locale(s)
*
* @throws \InvalidArgumentException If a locale contains invalid characters
*
* @deprecated since 2.3, to be removed in 3.0. Use setFallbackLocales() instead.
*
* @api
@ -144,6 +153,8 @@ class Translator implements TranslatorInterface
*
* @param array $locales The fallback locales
*
* @throws \InvalidArgumentException If a locale contains invalid characters
*
* @api
*/
public function setFallbackLocales(array $locales)
@ -151,6 +162,10 @@ class Translator implements TranslatorInterface
// needed as the fallback locales are linked to the already loaded catalogues
$this->catalogues = array();
foreach ($locales as $locale) {
$this->assertValidLocale($locale);
}
$this->fallbackLocales = $locales;
}
@ -175,6 +190,8 @@ class Translator implements TranslatorInterface
{
if (null === $locale) {
$locale = $this->getLocale();
} else {
$this->assertValidLocale($locale);
}
if (null === $domain) {
@ -197,6 +214,8 @@ class Translator implements TranslatorInterface
{
if (null === $locale) {
$locale = $this->getLocale();
} else {
$this->assertValidLocale($locale);
}
if (null === $domain) {
@ -279,4 +298,18 @@ class Translator implements TranslatorInterface
return array_unique($locales);
}
/**
* Asserts that the locale is valid, throws an Exception if not.
*
* @param string $locale Locale to tests
*
* @throws \InvalidArgumentException If the locale contains invalid characters
*/
private function assertValidLocale($locale)
{
if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
throw new \InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
}
}
}

View File

@ -28,6 +28,8 @@ interface TranslatorInterface
* @param string $domain The domain for the message
* @param string $locale The locale
*
* @throws \InvalidArgumentException If the locale contains invalid characters
*
* @return string The translated string
*
* @api
@ -43,6 +45,8 @@ interface TranslatorInterface
* @param string $domain The domain for the message
* @param string $locale The locale
*
* @throws \InvalidArgumentException If the locale contains invalid characters
*
* @return string The translated string
*
* @api
@ -54,6 +58,8 @@ interface TranslatorInterface
*
* @param string $locale The locale
*
* @throws \InvalidArgumentException If the locale contains invalid characters
*
* @api
*/
public function setLocale($locale);

View File

@ -176,7 +176,7 @@
</trans-unit>
<trans-unit id="47">
<source>This value should be the user current password.</source>
<target>This value should be the user current password.</target>
<target>This value should be the user's current password.</target>
</trans-unit>
<trans-unit id="48">
<source>This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.</source>

View File

@ -35,6 +35,7 @@ class Entity extends EntityParent implements EntityInterface
public $reference;
private $internal;
public $data = 'Overridden data';
public $initialized = false;
public function __construct($internal = null)
{

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\Validator\Tests;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\ExecutionContextInterface;
use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory;
use Symfony\Component\Validator\Constraints\Valid;
use Symfony\Component\Validator\Tests\Fixtures\Reference;
@ -561,4 +563,50 @@ class ValidationVisitorTest extends \PHPUnit_Framework_TestCase
$this->visitor->validate($entity, 'Default', '');
}
public function testInitializeObjectsOnFirstValidation()
{
$test = $this;
$entity = new Entity();
$entity->initialized = false;
// prepare initializers that set "initialized" to true
$initializer1 = $this->getMock('Symfony\\Component\\Validator\\ObjectInitializerInterface');
$initializer2 = $this->getMock('Symfony\\Component\\Validator\\ObjectInitializerInterface');
$initializer1->expects($this->once())
->method('initialize')
->with($entity)
->will($this->returnCallback(function ($object) {
$object->initialized = true;
}));
$initializer2->expects($this->once())
->method('initialize')
->with($entity);
$this->visitor = new ValidationVisitor('Root', $this->metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator(), null, array(
$initializer1,
$initializer2
));
// prepare constraint which
// * checks that "initialized" is set to true
// * validates the object again
$callback = function ($object, ExecutionContextInterface $context) use ($test) {
$test->assertTrue($object->initialized);
// validate again in same group
$context->validate($object);
// validate again in other group
$context->validate($object, '', 'SomeGroup');
};
$this->metadata->addConstraint(new Callback(array($callback)));
$this->visitor->validate($entity, 'Default', '');
$this->assertTrue($entity->initialized);
}
}

View File

@ -127,16 +127,19 @@ class ValidationVisitor implements ValidationVisitorInterface, GlobalExecutionCo
return;
}
// Initialize if the object wasn't initialized before
if (!isset($this->validatedObjects[$hash])) {
foreach ($this->objectInitializers as $initializer) {
if (!$initializer instanceof ObjectInitializerInterface) {
throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
}
$initializer->initialize($value);
}
}
// Remember validating this object before starting and possibly
// traversing the object graph
$this->validatedObjects[$hash][$group] = true;
foreach ($this->objectInitializers as $initializer) {
if (!$initializer instanceof ObjectInitializerInterface) {
throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
}
$initializer->initialize($value);
}
}
// Validate arrays recursively by default, otherwise every driver needs