Updating vendors

skala
Julio Montoya 11 years ago
parent 0b08b2b932
commit 5ff1c4e187
  1. 2
      vendor/autoload.php
  2. 13
      vendor/composer/autoload_files.php
  3. 1
      vendor/composer/autoload_namespaces.php
  4. 13
      vendor/composer/autoload_real.php
  5. 2836
      vendor/composer/installed.json
  6. 77
      vendor/flint/flint/CHANGELOG.md
  7. 19
      vendor/flint/flint/LICENSE
  8. 12
      vendor/flint/flint/README.md
  9. 37
      vendor/flint/flint/composer.json
  10. BIN
      vendor/flint/flint/doc/_static/peytzco.jpg
  11. 253
      vendor/flint/flint/doc/conf.py
  12. 304
      vendor/flint/flint/doc/index.rst
  13. 55
      vendor/flint/flint/src/Flint/Application.php
  14. 80
      vendor/flint/flint/src/Flint/Config/Configurator.php
  15. 77
      vendor/flint/flint/src/Flint/Config/Loader/JsonFileLoader.php
  16. 39
      vendor/flint/flint/src/Flint/Config/Normalizer/ChainNormalizer.php
  17. 33
      vendor/flint/flint/src/Flint/Config/Normalizer/EnvironmentNormalizer.php
  18. 15
      vendor/flint/flint/src/Flint/Config/Normalizer/NormalizerInterface.php
  19. 62
      vendor/flint/flint/src/Flint/Config/Normalizer/PimpleAwareNormalizer.php
  20. 40
      vendor/flint/flint/src/Flint/Config/ResourceCollection.php
  21. 39
      vendor/flint/flint/src/Flint/Console/Application.php
  22. 75
      vendor/flint/flint/src/Flint/Controller/Controller.php
  23. 56
      vendor/flint/flint/src/Flint/Controller/ControllerResolver.php
  24. 74
      vendor/flint/flint/src/Flint/Controller/ExceptionController.php
  25. 19
      vendor/flint/flint/src/Flint/PimpleAware.php
  26. 16
      vendor/flint/flint/src/Flint/PimpleAwareInterface.php
  27. 75
      vendor/flint/flint/src/Flint/Provider/ConfigServiceProvider.php
  28. 42
      vendor/flint/flint/src/Flint/Provider/FlintServiceProvider.php
  29. 87
      vendor/flint/flint/src/Flint/Provider/RoutingServiceProvider.php
  30. 1
      vendor/flint/flint/src/Flint/Resources/views/Exception/error.atom.twig
  31. 4
      vendor/flint/flint/src/Flint/Resources/views/Exception/error.css.twig
  32. 17
      vendor/flint/flint/src/Flint/Resources/views/Exception/error.html.twig
  33. 4
      vendor/flint/flint/src/Flint/Resources/views/Exception/error.js.twig
  34. 1
      vendor/flint/flint/src/Flint/Resources/views/Exception/error.json.twig
  35. 1
      vendor/flint/flint/src/Flint/Resources/views/Exception/error.rdf.twig
  36. 8
      vendor/flint/flint/src/Flint/Resources/views/Exception/error.txt.twig
  37. 3
      vendor/flint/flint/src/Flint/Resources/views/Exception/error.xml.twig
  38. 31
      vendor/flint/flint/src/Flint/Routing/Loader/NullLoader.php
  39. 6
      vendor/silex/silex/doc/changelog.rst
  40. 18
      vendor/silex/silex/doc/contributing.rst
  41. 19
      vendor/silex/silex/doc/cookbook/error_handler.rst
  42. 4
      vendor/silex/silex/doc/middlewares.rst
  43. 2
      vendor/silex/silex/doc/testing.rst
  44. 4
      vendor/silex/silex/doc/usage.rst
  45. 2
      vendor/silex/silex/src/Silex/Application.php
  46. 4
      vendor/silex/silex/src/Silex/Provider/DoctrineServiceProvider.php
  47. 1
      vendor/silex/silex/src/Silex/RedirectableUrlMatcher.php
  48. 2
      vendor/silex/silex/src/Silex/ServiceProviderInterface.php
  49. 15
      vendor/silex/silex/tests/Silex/Tests/ApplicationTest.php
  50. 5
      vendor/silex/silex/tests/Silex/Tests/Provider/ValidatorServiceProviderTest.php
  51. 1
      vendor/symfony/debug/Symfony/Component/Debug/ErrorHandler.php
  52. 6
      vendor/symfony/debug/Symfony/Component/Debug/Exception/ContextErrorException.php
  53. 4
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Crawler.php
  54. 1
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Form.php
  55. 2
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Link.php
  56. 5
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/LinkTest.php
  57. 1
      vendor/symfony/finder/Symfony/Component/Finder/Tests/Iterator/RealIteratorTestCase.php
  58. 54
      vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Request.php
  59. 1
      vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/RequestMatcher.php
  60. 5
      vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php
  61. 14
      vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
  62. 31
      vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Tests/RequestTest.php
  63. 3
      vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
  64. 1
      vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php
  65. 8
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php
  66. 2
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/DependencyInjection/ConfigurableExtension.php
  67. 1
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php
  68. 1
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/EventListener/EsiListener.php
  69. 8
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php
  70. 8
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php
  71. 2
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/HttpKernel.php
  72. 6
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Kernel.php
  73. 4
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php
  74. 15
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php
  75. 6
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php
  76. 7
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/composer.json
  77. 2
      vendor/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php
  78. 59
      vendor/symfony/process/Symfony/Component/Process/Process.php
  79. 2
      vendor/symfony/process/Symfony/Component/Process/ProcessUtils.php
  80. 23
      vendor/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php
  81. 16
      vendor/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php
  82. 4
      vendor/symfony/process/Symfony/Component/Process/Tests/SignalListener.php
  83. 2
      vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGenerator.php
  84. 26
      vendor/symfony/routing/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php
  85. 32
      vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php
  86. 17
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit6849b8aa9cb29cb82589890fae069cb3::getLoader();
return ComposerAutoloaderInit1f12a82cc2ba6cc62e0ea30ebe1f1130::getLoader();

@ -0,0 +1,13 @@
<?php
// autoload_files.php generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
$vendorDir . '/symfony/intl/Symfony/Component/Intl/Resources/stubs/functions.php',
$vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
$vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
$vendorDir . '/kriswallsmith/assetic/src/functions.php',
);

@ -59,6 +59,7 @@ return array(
'Gedmo' => array($vendorDir . '/gedmo/doctrine-extensions/lib'),
'Gaufrette' => array($vendorDir . '/knplabs/gaufrette/src'),
'FranMoreno' => array($vendorDir . '/franmomu/silex-pagerfanta-provider/src'),
'Flint' => array($vendorDir . '/flint/flint/src'),
'Entity' => array($baseDir . '/main/inc'),
'Doctrine\\ORM' => array($vendorDir . '/doctrine/orm/lib'),
'Doctrine\\DBAL\\Migrations' => array($vendorDir . '/doctrine/migrations/lib'),

@ -2,7 +2,7 @@
// autoload_real.php generated by Composer
class ComposerAutoloaderInit6849b8aa9cb29cb82589890fae069cb3
class ComposerAutoloaderInit1f12a82cc2ba6cc62e0ea30ebe1f1130
{
private static $loader;
@ -19,9 +19,9 @@ class ComposerAutoloaderInit6849b8aa9cb29cb82589890fae069cb3
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit6849b8aa9cb29cb82589890fae069cb3', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit1f12a82cc2ba6cc62e0ea30ebe1f1130', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit6849b8aa9cb29cb82589890fae069cb3', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit1f12a82cc2ba6cc62e0ea30ebe1f1130', 'loadClassLoader'));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
@ -38,10 +38,9 @@ class ComposerAutoloaderInit6849b8aa9cb29cb82589890fae069cb3
$loader->register(true);
require $vendorDir . '/symfony/intl/Symfony/Component/Intl/Resources/stubs/functions.php';
require $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php';
require $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php';
require $vendorDir . '/kriswallsmith/assetic/src/functions.php';
foreach (require __DIR__ . '/autoload_files.php' as $file) {
require $file;
}
return $loader;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,77 @@
1.4.0 / 2013-07-04
==================
* Configuration cache is now automatically bursted when debug is true and a file have changed.
* Introduce a ResourceCollection object that ConfigLoaders must add Resources to when loading. This
caused a BC break in the Configurator constructor arguments.
* Debug and cache dir is now setters on the Configurator instead of constructor arguments.
* Changed package name to `flint/flint`.
1.3.1 / 2013-06-26
==================
* PHP Scalar values are now converted to its string counter part `true` will be `'true'` and so forth.
* Replacement for `%placeholder%` and `#PLACEHOLDER#` have been made more robust. Also `%placeholder%` only matches
lowercase characters and `#PLACEHOLDER#` only matches uppercase characters.
1.3.0 / 2013-06-26
==================
* New documentation in Sphinx which is hosted on readthedocs.org
* Configuration with JSON files and caching.
1.2.0 / 2013-05-06
==================
* `ApplicationAwareInterface` and `ApplicationAware` was renamed to `PimpleAwareInterface` and `PimpleAware`. This
means Controller must use `$this->pimple` instead of `$this->app` when accessing services. The base Controller have
been updated.
* Add a `Flint\Console\Application` base class that gives access to `pimple` through `PimpleAwareInterface`. It takes
a `Pimple` object as the first constructor argument.
1.0.6 / 2013-03-03
==================
* Allow setting parameters in the constructor
* Just a small typo fix in README.md
* Reshare extended services
* allow 5.5 failures
* Add section about twig bridge
* test the priority of registered service providers
* rearrange service providers
1.0.5 / 2013-02-08
==================
* Alias url_generator service to router to be compatible with application using UrlGeneratorServiceProvider
* New documentation
1.0.4 / 2013-02-04
==================
* Add test for overriding exception controller
* Update documentation
* Allow to override the controller used for excetion handling
1.0.3 / 2013-02-03
==================
* %root_dir%/config and %root_dir% are now registered as locations for config locator
* Ignore coverage directory
* Normalize visibility in Controller
* Ignore local phpunit.xml file
* Restructure composer file
* Add testcase for matcher and matcher_base_class
* Add .gitattributes file to make downloads slimmer
1.0.2 / 2013-01-31
==================
* Fix typo in RoutingServiceProvider which previously would use an invalid class for matching.
* Add matcher_base_class as a default option for `routing.options` to make the cached matcher extend from the correct class.
1.0.1 / 2013-01-31
==================
* Add .gitattributes file to make downloads slimmer
* Add CHANGELOG.md

@ -0,0 +1,19 @@
Copyright (c) 2013 Henrik Bjornskov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -0,0 +1,12 @@
Flint
=====
[![Build Status](https://travis-ci.org/henrikbjorn/Flint.png?branch=master)](https://travis-ci.org/henrikbjorn/Flint)
Flint is a microframework build on top of Silex. It brings the full Router, conventions and structure to Silex.
You can read more about [in it documentation](http://flint.rtfd.org).
---
[![Peytz & Co](doc/_static/peytzco.jpg)](http://peytz.dk)

@ -0,0 +1,37 @@
{
"name" : "flint/flint",
"description" : "Enhanced Silex",
"license" : "MIT",
"authors" : [{
"name" : "Henrik Bjornskov",
"email" : "henrik@bjrnskov.dk"
}],
"autoload" : {
"psr-0" : {
"Flint" : "src"
}
},
"replace" : {
"henrikbjorn/flint" : "self.version"
},
"require" : {
"php" : ">=5.3.3",
"silex/silex" : "~1.0",
"twig/twig" : "~1.10",
"symfony/config" : "~2.2"
},
"require-dev" : {
"symfony/console" : "~2.2"
},
"extra" : {
"branch-alias" : {
"dev-master" : "1.4.x-dev"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -0,0 +1,253 @@
# -*- coding: utf-8 -*-
#
# Flint documentation build configuration file, created by
# sphinx-quickstart on Tue May 21 08:39:31 2013.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
from subprocess import Popen, PIPE
def get_version():
""" Returns project version as string from 'git describe' command. """
pipe = Popen('git describe --tags --always', stdout=PIPE, shell=True)
version = pipe.stdout.read()
if version:
return version
else:
return 'latest'
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Flint'
copyright = u'2013, Henrik Bjrnskov'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = get_version().lstrip('v')
# The full version, including alpha/beta/rc tags.
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Flintdoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Flint.tex', u'Flint Documentation',
u'Henrik Bjrnskov', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'flint', u'Flint Documentation',
[u'Henrik Bjrnskov'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Flint', u'Flint Documentation',
u'Henrik Bjrnskov', 'Flint', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

@ -0,0 +1,304 @@
Flint
=====
Flint is a microframework built on top of Silex. It tries to bridge the gap between Silex and
Symfony by bringing structure and conventions to Silex.
Getting started
---------------
To start a new project with Flint the easiest way is to use
`Composer <http://getcomposer.org>`__ and
`Flint-Skeleton <http://github.com/henrikbjorn/flint-skeleton>`__.
.. code-block:: bash
$ php composer.phar create-project -s dev henrikbjorn/flint-skeleton my-flint-application
Or if you are migrating from a Silex project you can change your
``composer.json`` file to require Flint and change the Application class
that is used.
.. code-block:: bash
$ php composer.phar require flint/flint:~1.0
.. code-block:: php
<?php
use Flint\Application;
$application = new Application($rootDir, $debug);
It is recommended to subclass ``Flint\Application`` instead of using the
application class directly.
Controllers
-----------
Flint tries to make Silex more like Symfony. And by using closures it is
hard to seperate controllers in a logic way when you have more than a
couple of them. To make it better it is recommended to use classes and
methods for controllers. The basics is `explained
here <http://silex.sensiolabs.org/doc/usage.html#controllers-in-classes>`__
but Flint takes it further and allows the application to be injected
into a controller.
The first way to accomplish this is by implementing
``PimpleAwareInterface`` or extending ``PimpleAware``. This works
exactly `as described in
Symfony <http://symfony.com/doc/2.0/book/controller.html#the-base-controller-class>`__.
With the only exception that the property is called ``$pimple`` instead
of ``$container``.
.. code-block:: php
<?php
namespace Acme\Controller;
use Flint\PimpleAware;
class HelloController extends PimpleAware
{
public function indexAction()
{
return $this->pimple['twig']->render('Hello/index.html.twig');
}
}
Another way is to use a base controller which have convenience methods
for the most frequently used services. Theese methods can be seen in the
source code if you look at the implementation for
``Flint\Controller\Controller``.
.. code-block:: php
<?php
namespace Acme\Controller;
use Flint\Controller\Controller;
class HelloController extends Controller
{
public function indexAction()
{
return $this->render('Hello/index.html.twig');
}
}
Routing
-------
Because Flint replaces the url matcher used in Symfony with the full
router implementation a lot of new things is possible.
Caching is one of thoose things. It makes your application faster as it
does not need to register routes on every request. Together with loading
your routes from a configuration file like Symfony it will also bring
more structure to your application.
To enable caching you just need to point the router to the directory you
want to use and if it should cache or not. By default the ``debug``
parameter will be used as to determaine if cache should be used or not.
.. code-block:: php
<?php
// .. create a $app before this line
$app->inject(array(
'routing.options' => array(
'cache_dir' => '/my/cache/directory/routing',
),
));
Before it is possible to use the full power of caching it is needed to
use configuration files because Silex will always call add routes via
its convenience methods ``get|post|delete|put``. Furtunately this is
baked right in.
.. code-block:: php
<?php
// .. create $app
$app->inject(array(
'routing.resource' => 'config/routing.xml',
));
.. code-block:: xml
<!-- config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="homepage" pattern="/">
<default key="_controller">Acme\\Controller\\DefaultController::indexAction</default>
</route>
</routes>
This will make the router load that resource by default. Here xml is
used as an example but ``php`` is also supported together with ``yml``
if ``Symfony\Component\Yaml\Yaml`` is autoloadable.
The benefit from doing it this way is of course they can be cached but
also it allows you to import routing files that are included in
libraries and even other Symfony bundles such as the
`WebProfilerBundle <https://github.com/symfony/webprofilerbundle>`__.
Also it will make it easier to generate routes from inside your views.
.. code-block:: jinja
<a href="{{ app.router.generate('homepage') }}">Homepage</a>
This is also possible with Silex but with a more verbose syntax. The
syntax can be even more precise by using the twig functions that is
available in the Twig bridge for Symfony. To enable thoose add the twig
bridge to your composer file.
.. code-block:: json
{
"require" : {
"symfony/twig-bridge" : "~2.0"
}
}
Now it is possible to use the functions inside your Twig templates.
.. code-block:: jinja
<a href="{{ path('homepage') }}">Homepage</a>
<a href="{{ url('homepage') }}">Homepage</a>
Default Parameters
------------------
The two contructor arguments ``$rootDir`` and ``$debug`` are also
registered on the application as parameters. This makes it easier for
services to add paths for caching, logs or other directories.
.. code-block:: php
<?php
// .. create $app
$app->inject(array(
'twig.path' => $app['root_dir'] . '/views',
));
Custom Error Pages
------------------
When finished a project or application it is the small things that
matter the most. Such as having a custom error page instead of the one
Silex provides by default. Also it can help a lost user navigate back.
Flint makes this possible by using the exception handler from Symfony
and a dedicated controller. Both the views and the controller can be
overrriden.
This will only work when debug is turned off.
To override the error pages the same logic is used as inside Symfony.
The logic is very well described `in their
documentation <http://symfony.com/doc/master/cookbook/controller/error_pages.html>`__.
Only difference from Symfony is the templates must be created inside
``views/Exception/`` directory. Inside the templates there is access to
``app`` which in turns gives you access to all of the services defined.
To override the controller used by the exception handler change the
``exception_controller`` parameter. This parameter will by default be
set to ``Flint\\Controller\\ExceptionController::showAction``.
.. code-block:: php
<?php
// .. create $app
$app->inject(array(
'exception_controller' => 'Acme\\Controller\\ExceptionController::showAction',
));
To see what parameter the controller action takes look at the one
provided by default. Normally it should not be overwritten as it already
gives a lot of flexibilty with the template lookup.
Injecting Configuration Parameters
----------------------------------
Some times it is more useful to inject an array of parameters instead of
setting them on the application one-by-one. Flint have a method that
does this. It does the same thing as the second parameter of Silex
``register`` method.
.. code-block:: php
<?php
// .. $app
$app->inject(array(
'twig.paths' => '/my/path/to/views',
));
Pimple Console
--------------
``Flint\Console\Application`` is an extension of the base console
application shipped with Symfony. It gives access to Pimple in commands.
.. code-block:: php
<?php
namespace Application\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class MyCommand extends \Symfony\Component\Console\Command\Command
{
protected function execute(InputInterface $input, OutputInterface $output)
{
$pimple = $this->getApplication()->getPimple();
}
}
Configuration
-------------
Every application need to have some parameters configured based on environment or other parameters.
Flint comes with a ``Configurator`` which reads ``json`` files and sets them as parmeters on your application.
It is very easy to use:
.. code-block:: php
<?php
use Flint\Application;
$app = new Application($rootDir, $debug);
$app->configure('config.json');
// Or use the service directly
$app['configurator']->configure($app, 'app/config/prod.json');
The Configurator will replace placeholders marked with ``%my_parameter%`` with the corresponding parameter in your
application which in this instance would be ``$app['my_parameter']``. It will also replace placeholders marked as
``#my_env#`` with environment variables.
It is possible to inherit from a config file by using a special key ``@import`` and set its value to another file. The
loaded parameters from ``@import`` will have the lowest priority when merging the two files.
.. warning::
When using Silex version 1.0.0 or earlier it is not possible to load configurations in the boot method. This is because
when adding a listener to the `dispatcher` service it will get the routes and a bunch of other services which means it
is too late.

@ -0,0 +1,55 @@
<?php
namespace Flint;
use Flint\Provider\ConfigServiceProvider;
use Flint\Provider\FlintServiceProvider;
use Flint\Provider\RoutingServiceProvider;
use Silex\Provider\TwigServiceProvider;
/**
* @package Flint
*/
class Application extends \Silex\Application
{
/**
* Assigns rootDir and debug to the pimple container. Also replaces the
* normal resolver with a PimpleAware Resolver.
*
* @param string $rootDir
* @param boolean $debug
* @param array $parameters
*/
public function __construct($rootDir, $debug = false, array $parameters = array())
{
parent::__construct($parameters);
$this['root_dir'] = $rootDir;
$this['debug'] = $debug;
$this->register(new ConfigServiceProvider);
$this->register(new RoutingServiceProvider);
$this->register(new TwigServiceProvider);
$this->register(new FlintServiceProvider);
}
/**
* @see Flint\Config\Configurator::configure()
* @param string $resource
*/
public function configure($resource)
{
$this['configurator']->configure($this, $resource);
}
/**
* @deprecated
* @param array $parameters
*/
public function inject(array $parameters)
{
foreach ($parameters as $k => $v) {
$this[$k] = $v;
}
}
}

@ -0,0 +1,80 @@
<?php
namespace Flint\Config;
use Pimple;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Loader\LoaderInterface;
/**
* @package Flint
*/
class Configurator
{
protected $loader;
protected $resources;
protected $cacheDir;
protected $debug = true;
/**
* @param LoaderInterface $resolver
* @param ResourceCollection $resources
* @param string $cacheDir
* @param boolean $debug
*/
public function __construct(LoaderInterface $resolver, ResourceCollection $resources)
{
$this->resources = $resources;
$this->loader = $resolver;
}
/**
* @param boolean $debug
*/
public function setDebug($debug)
{
$this->debug = (boolean) $debug;
}
/**
* @param string $cacheDir
*/
public function setCacheDir($cacheDir)
{
$this->cacheDir = $cacheDir;
}
/**
* @param Pimple $pimple
* @param string $resource
*/
public function configure(Pimple $pimple, $resource)
{
$cache = new ConfigCache($this->cacheDir . '/' . crc32($resource) . '.php', $this->debug);
if (!$cache->isFresh()) {
$parameters = $this->loader->load($resource);
}
if ($this->cacheDir && isset($parameters)) {
$cache->write('<?php $parameters = ' . var_export($parameters, true) . ';', $this->resources->all());
}
if (!isset($parameters)) {
require (string) $cache;
}
$this->build($pimple, $parameters);
}
/**
* @param Pimple $pimple
* @param array $parameters
*/
protected function build(Pimple $pimple, array $parameters)
{
foreach ($parameters as $key => $value) {
$pimple[$key] = $value;
}
}
}

@ -0,0 +1,77 @@
<?php
namespace Flint\Config\Loader;
use Flint\Config\Normalizer\NormalizerInterface;
use Flint\Config\ResourceCollection;
use Symfony\Component\Config\FileLocatorInterface;
use Symfony\Component\Config\Resource\FileResource;
/**
* @package Flint
*/
class JsonFileLoader extends \Symfony\Component\Config\Loader\FileLoader
{
protected $normalizer;
protected $resources;
/**
* @param NormalizerInterface $normalizer
* @param FileLocatorInterface $locator
*/
public function __construct(
NormalizerInterface $normalizer,
FileLocatorInterface $locator,
ResourceCollection $resources
) {
parent::__construct($locator);
$this->normalizer = $normalizer;
$this->resources = $resources;
}
/**
* {@inheritDoc}
*/
public function load($resource, $type = null)
{
$resource = $this->locator->locate($resource);
$this->resources->add(new FileResource($resource));
return $this->parse($this->read($resource), $resource);
}
/**
* @param array $parameters
* @param string $file
* @return array
*/
protected function parse(array $parameters, $file)
{
if (!isset($parameters['@import'])) {
return $parameters;
}
$import = $parameters['@import'];
unset($parameters['@import']);
$this->setCurrentDir(dirname($import));
return array_replace($this->import($import, null, false, $file), $parameters);
}
protected function read($resource)
{
return json_decode($this->normalizer->normalize(file_get_contents($resource)), true);
}
/**
* {@inheritDoc}
*/
public function supports($resource, $type = null)
{
return is_string($resource) && 'json' === pathinfo($resource, PATHINFO_EXTENSION);
}
}

@ -0,0 +1,39 @@
<?php
namespace Flint\Config\Normalizer;
/**
* @package Flint
*/
class ChainNormalizer implements NormalizerInterface
{
protected $normalizers = array();
/**
* @param array $normalizers
*/
public function __construct(array $normalizers = array())
{
array_map(array($this, 'add'), $normalizers);
}
/**
* @param NormalizerInterface $normalizer
*/
public function add(NormalizerInterface $normalizer)
{
$this->normalizers[] = $normalizer;
}
/**
* {@inheritDoc}
*/
public function normalize($contents)
{
foreach ($this->normalizers as $normalizer) {
$contents = $normalizer->normalize($contents);
}
return $contents;
}
}

@ -0,0 +1,33 @@
<?php
namespace Flint\Config\Normalizer;
/**
* @package Flint
*/
class EnvironmentNormalizer implements NormalizerInterface
{
const PLACEHOLDER = '{##|#([A-Z0-9_]+)#}';
/**
* @param string $contents
* @return string
*/
public function normalize($contents)
{
return preg_replace_callback(static::PLACEHOLDER, array($this, 'callback'), $contents);
}
/**
* @param array $matches
* @return mixed
*/
protected function callback($matches)
{
if (!isset($matches[1])) {
return '##';
}
return getenv($matches[1]);
}
}

@ -0,0 +1,15 @@
<?php
namespace Flint\Config\Normalizer;
/**
* @package Flint
*/
interface NormalizerInterface
{
/**
* @param string $contents
* @return string
*/
public function normalize($contents);
}

@ -0,0 +1,62 @@
<?php
namespace Flint\Config\Normalizer;
use Pimple;
/**
* @package Flint
*/
class PimpleAwareNormalizer extends \Flint\PimpleAware implements NormalizerInterface
{
const PLACEHOLDER = '{%%|%([a-z0-9_.]+)%}';
/**
* @param Pimple $pimple
*/
public function __construct(Pimple $pimple = null)
{
$this->setPimple($pimple);
}
/**
* @param string $contents
* @return string
*/
public function normalize($contents)
{
return preg_replace_callback(static::PLACEHOLDER, array($this, 'callback'), $contents);
}
/**
* @param array $matches
* @return mixed
*/
protected function callback($matches)
{
if (!isset($matches[1])) {
return '%%';
}
return $this->scarlarToString($this->pimple[$matches[1]]);
}
/**
* @param mixed $value
* @return mixed
*/
protected function scarlarToString($value)
{
switch (gettype($value)) {
case 'resource':
case 'object':
throw new \RuntimeException('Unable to replace placeholder if its replacement is an object or resource.');
case 'boolean':
return $value ? 'true' : 'false';
case 'NULL':
return 'null';
default:
return (string) $value;
}
}
}

@ -0,0 +1,40 @@
<?php
namespace Flint\Config;
use Symfony\Component\Config\Resource\ResourceInterface;
/**
* Collection of Resources that is used to burst the cache for the configs
* when in debug.
*
* @package Flint
*/
class ResourceCollection
{
protected $resources = array();
/**
* @param array $resources
*/
public function __construct(array $resources = array())
{
array_map(array($this, 'add'), $resources);
}
/**
* @param ResourceInterface $resource
*/
public function add(ResourceInterface $resource)
{
$this->resources[] = $resource;
}
/**
* @return ResourceInterface[]
*/
public function all()
{
return $this->resources;
}
}

@ -0,0 +1,39 @@
<?php
namespace Flint\Console;
use Flint\PimpleAwareInterface;
use Symfony\Component\Console\Application as BaseApplication;
class Application extends BaseApplication implements PimpleAwareInterface
{
protected $pimple;
/**
* @param Pimple $pimple
* @param string $name
* @param string $version
*/
public function __construct(\Pimple $pimple, $name = 'Flint', $version = 'UNKNOWN')
{
parent::__construct($name, $version);
$this->setPimple($pimple);
}
/**
* @return Pimple
*/
public function getPimple()
{
return $this->pimple;
}
/**
* {@inheritDoc}
*/
public function setPimple(\Pimple $pimple = null)
{
$this->pimple = $pimple;
}
}

@ -0,0 +1,75 @@
<?php
namespace Flint\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* @package Flint
*/
abstract class Controller extends \Flint\PimpleAware
{
/**
* Creates a normal response with the given text and statusCode
*
* @param string $text
* @param integer $statusCode
* @param array $headers
* @return Response
*/
public function text($text, $statusCode = 200, array $headers = array())
{
return new Response($text, $statusCode, $headers);
}
/**
* @see Symfony\Component\Routing\RouterInterface::generate()
*/
public function generateUrl($name, array $parameters = array(), $reference = UrlGeneratorInterface::ABSOLUTE_PATH)
{
return $this->pimple['router']->generate($name, $parameters, $reference);
}
/**
* @see Twig_Environment::render();
*/
public function render($name, array $context = array())
{
return $this->pimple['twig']->render($name, $context);
}
/**
* @see Silex\Application::redirect()
*/
public function redirect($url, $statusCode = 302)
{
return $this->pimple->redirect($url, $statusCode);
}
/**
* @see Silex\Application::abort()
*/
public function abort($statusCode, $message = '', array $headers = array())
{
return $this->pimple->abort($statusCode, $message, $headers);
}
/**
* @param string $id
* @return boolean
*/
public function has($id)
{
return isset($this->pimple[$id]);
}
/**
* @param string $id
* @return mixed
*/
public function get($id)
{
return $this->pimple[$id];
}
}

@ -0,0 +1,56 @@
<?php
namespace Flint\Controller;
use Flint\Application;
use Flint\PimpleAwareInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
/**
* Injects the Application into the Controller if it implements the right interface
* otherwise it delegates to the composed resolver.
*
* @package Flint
*/
class ControllerResolver implements ControllerResolverInterface
{
protected $pimple;
protected $resolver;
/**
* @param ControllerResolverInterface $resolver
* @param Pimple $pimple
*/
public function __construct(ControllerResolverInterface $resolver, \Pimple $pimple)
{
$this->resolver = $resolver;
$this->pimple = $pimple;
}
/**
* {@inheritDoc}
*/
public function getController(Request $request)
{
$controller = $this->resolver->getController($request);
if (false == is_array($controller)) {
return $controller;
}
if ($controller[0] instanceof PimpleAwareInterface) {
$controller[0]->setPimple($this->pimple);
}
return $controller;
}
/**
* {@inheritDoc}
*/
public function getArguments(Request $request, $controller)
{
return $this->resolver->getArguments($request, $controller);
}
}

@ -0,0 +1,74 @@
<?php
namespace Flint\Controller;
use Symfony\Component\HttpKernel\Debug\ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* @package Flint
*/
class ExceptionController extends Controller
{
/**
* @param Request $request
* @param FlattenException $exception
* @param string $format
*/
public function showAction(Request $request, FlattenException $exception, $format)
{
$handler = new ExceptionHandler($this->pimple['debug']);
if ($this->pimple['debug']) {
return $handler->createResponse($exception);
}
$code = $exception->getStatusCode();
$template = $this->resolve($request, $code, $format);
if ($template) {
$contents = $this->pimple['twig']->render($template, array(
'status_code' => $code,
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
'exception' => $exception,
));
return new Response($contents, $code);
}
return $handler->createResponse($exception);
}
/**
* @param Request $request
* @param integer $code
* @param string $format
* @return string|null
*/
protected function resolve(Request $request, $code, $format)
{
$loader = $this->pimple['twig.loader'];
$templates = array(
'Exception/error' . $code . '.' . $format . '.twig',
'Exception/error.' . $format . '.twig',
'Exception/error.html.twig',
'@Flint/Exception/error.' . $format . '.twig',
'@Flint/Exception/error.html.twig',
);
foreach ($templates as $template) {
if (false == $loader->exists($template)) {
continue;
}
if (strpos($template, '.html.twig')) {
$request->setRequestFormat('html');
}
return $template;
}
}
}

@ -0,0 +1,19 @@
<?php
namespace Flint;
/**
* @package Flint
*/
abstract class PimpleAware implements PimpleAwareInterface
{
protected $pimple;
/**
* {@inheritDoc}
*/
public function setPimple(\Pimple $pimple = null)
{
$this->pimple = $pimple;
}
}

@ -0,0 +1,16 @@
<?php
namespace Flint;
/**
* A Silex version of ContainerAwareInterface that is found in Symfony
*
* @package Flint
*/
interface PimpleAwareInterface
{
/**
* @param Pimple|null $flint
*/
public function setPimple(\Pimple $pimple = null);
}

@ -0,0 +1,75 @@
<?php
namespace Flint\Provider;
use Flint\Config\Configurator;
use Flint\Config\ResourceCollection;
use Flint\Config\Loader\JsonFileLoader;
use Flint\Config\Normalizer\ChainNormalizer;
use Flint\Config\Normalizer\EnvironmentNormalizer;
use Flint\Config\Normalizer\PimpleAwareNormalizer;
use Silex\Application;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\Loader\LoaderResolver;
/**
* @package Flint
*/
class ConfigServiceProvider implements \Silex\ServiceProviderInterface
{
/**
* {@inheritDoc}
*/
public function register(Application $app)
{
$app['config.paths'] = function (Application $app) {
return array($app['root_dir'] . '/config', $app['root_dir']);
};
$app['config.locator'] = $app->share(function (Application $app) {
return new FileLocator($app['config.paths']);
});
$app['config.resource_collection'] = $app->share(function (Application $app) {
return new ResourceCollection;
});
$app['config.normalizer'] = $app->share(function (Application $app) {
$normalizer = new ChainNormalizer;
$normalizer->add(new PimpleAwareNormalizer($app));
$normalizer->add(new EnvironmentNormalizer);
return $normalizer;
});
$app['config.loader'] = $app->share(function (Application $app) {
return new DelegatingLoader($app['config.loader_resolver']);
});
$app['config.loader_resolver'] = $app->share(function ($app) {
$loader = new JsonFileLoader($app['config.normalizer'], $app['config.locator'], $app['config.resource_collection']);
return new LoaderResolver(array($loader));
});
$app['configurator'] = $app->share(function (Application $app) {
$configurator = new Configurator($app['config.loader'], $app['config.resource_collection']);
$configurator->setCacheDir($app['config.cache_dir']);
$configurator->setDebug($app['debug']);
return $configurator;
});
if (!isset($app['config.cache_dir'])) {
$app['config.cache_dir'] = null;
}
}
/**
* {@inheritDoc}
*/
public function boot(Application $app)
{
}
}

@ -0,0 +1,42 @@
<?php
namespace Flint\Provider;
use Symfony\Component\HttpKernel\EventListener\ExceptionListener;
use Flint\Controller\ControllerResolver;
use Silex\Application;
/**
* @package Flint
*/
class FlintServiceProvider implements \Silex\ServiceProviderInterface
{
/**
* {@inheritDoc}
*/
public function register(Application $app)
{
$app['exception_controller'] = 'Flint\\Controller\\ExceptionController::showAction';
$app['exception_handler'] = $app->share(function ($app) {
return new ExceptionListener($app['exception_controller'], $app['logger']);
});
$app['resolver'] = $app->share($app->extend('resolver', function ($resolver, $app) {
return new ControllerResolver($resolver, $app);
}));
$app['twig.loader.filesystem'] = $app->share($app->extend('twig.loader.filesystem', function ($loader, $app) {
$loader->addPath(__DIR__ . '/../Resources/views', 'Flint');
return $loader;
}));
}
/**
* {@inheritDoc}
*/
public function boot(Application $app)
{
}
}

@ -0,0 +1,87 @@
<?php
namespace Flint\Provider;
use Silex\Application;
use Flint\Routing\Loader\NullLoader;
use Symfony\Component\Routing\Router;
use Symfony\Component\Routing\Loader\XmlFileLoader;
use Symfony\Component\Routing\Loader\PhpFileLoader;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Config\Loader\DelegatingLoader;
/**
* @package Flint
*/
class RoutingServiceProvider implements \Silex\ServiceProviderInterface
{
/**
* {@inheritDoc}
*/
public function register(Application $app)
{
$app['routing.resource'] = null;
$app['routing.options'] = array();
$app['routing.loader.xml'] = $app->share(function (Application $app) {
return new XmlFileLoader($app['config.locator']);
});
$app['routing.loader.php'] = $app->share(function (Application $app) {
return new PhpFileLoader($app['config.locator']);
});
$app['routing.loader.yml'] = $app->share(function (Application $app) {
return new YamlFileLoader($app['config.locator']);
});
$app['routing.loader.null'] = $app->share(function (Application $app) {
return new NullLoader;
});
$app['routing.loader.resolver'] = $app->share(function (Application $app) {
$loaders = array(
$app['routing.loader.xml'],
$app['routing.loader.php'],
$app['routing.loader.null'],
);
if (class_exists('Symfony\\Component\\Yaml\\Yaml')) {
$loaders[] = $app['routing.loader.yml'];
}
return new LoaderResolver($loaders);
});
$app['routing.loader'] = $app->share(function (Application $app) {
return new DelegatingLoader($app['routing.loader.resolver']);
});
$app['router'] = $app->share(function (Application $app) {
$defaults = array(
'debug' => $app['debug'],
'matcher_base_class' => 'Silex\\RedirectableUrlMatcher',
'matcher_class' => 'Silex\\RedirectableUrlMatcher',
);
$options = array_replace($defaults, $app['routing.options']);
return new Router($app['routing.loader'], $app['routing.resource'], $options, $app['request_context'], $app['logger']);
});
$app['routes'] = function (Application $app) {
return $app['router']->getRouteCollection();
};
$app['url_matcher'] = $app->raw('router');
$app['url_generator'] = $app->raw('router');
}
/**
* {@inheritDoc}
*/
public function boot(Application $app)
{
}
}

@ -0,0 +1 @@
{% include 'Exception/error.xml.twig' with { 'exception': exception } %}

@ -0,0 +1,4 @@
/*
{{ status_code }} {{ status_text }}
*/

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>An Error Occurred: {{ status_text }}</title>
</head>
<body>
<h1>Oops! An Error Occurred</h1>
<h2>The server returned a "{{ status_code }} {{ status_text }}".</h2>
<div>
Something is broken. Please e-mail us at [email] and let us know
what you were doing when this error occurred. We will fix it as soon
as possible. Sorry for any inconvenience caused.
</div>
</body>
</html>

@ -0,0 +1,4 @@
/*
{{ status_code }} {{ status_text }}
*/

@ -0,0 +1 @@
{{ { 'error': { 'code': status_code, 'message': status_text } }|json_encode|raw }}

@ -0,0 +1 @@
{% include 'Exception/error.xml.twig' with { 'exception': exception } %}

@ -0,0 +1,8 @@
Oops! An Error Occurred
=======================
The server returned a "{{ status_code }} {{ status_text }}".
Please e-mail us at [email] and let us know what you were doing when this
error occurred. We will fix it as soon as possible. Sorry for any
inconvenience caused.

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="{{ _charset }}" ?>
<error code="{{ status_code }}" message="{{ status_text }}" />

@ -0,0 +1,31 @@
<?php
namespace Flint\Routing\Loader;
use Symfony\Component\Routing\RouteCollection;
/**
* When `routing.resource` is null a loader needs to support that.
* This just returns an empty RouteCollection so an exception will
* not be raised and to keep BC with Silex.
*
* @package Flint
*/
class NullLoader extends \Symfony\Component\Config\Loader\Loader
{
/**
* {@inheritDoc}
*/
public function load($resource, $type = null)
{
return new RouteCollection;
}
/**
* {@inheritDoc}
*/
public function supports($resource, $type = null)
{
return null === $resource;
}
}

@ -1,6 +1,12 @@
Changelog
=========
1.0.1 (2013-07-04)
------------------
* Fixed RedirectableUrlMatcher::redirect() when Silex is configured to use a logger
* Make ``DoctrineServiceProvider`` multi-db support lazy.
1.0.0 (2013-05-03)
------------------

@ -14,7 +14,7 @@ steps.
* Optionally, add some technical documentation.
* Send a pull request. Bonus points for topic branches.
* Send a pull request, to the correct `target branch`_. Bonus points for topic branches.
If you have a big change or would like to discuss something,
please join us on the `mailing list
@ -25,6 +25,22 @@ please join us on the `mailing list
Any code you contribute must be licensed under the MIT
License.
Target branch
=============
Before you create a pull request for Silex, you need to determine which branch
to submit it to. Read this section carefully first.
Silex has two active branches: `1.0` and `master` (`1.1`).
* **1.0**: Bugfixes and documentation fixes go into the 1.0 branch. 1.0 is
periodically merged into master. The 1.0 branch targets versions 2.1, 2.2 and
2.3 of Symfony2.
* **1.1**: All new features go into the 1.1 branch. Changes cannot break
backward compatibility. The 1.1 branch targets the 2.3 version of Symfony2.
Writing Documentation
=====================

@ -29,8 +29,19 @@ You register it by calling the static ``register`` method::
It is recommended that you do this in your front controller, i.e.
``web/index.php``.
.. note::
Handling fatal errors
---------------------
The ``ErrorHandler`` has nothing to do with the ``ExceptionHandler``. The
``ExceptionHandler`` is responsible for displaying caught exceptions
nicely.
To handle fatal errors, you can additionally register a global
``ExceptionHandler``::
use Symfony\Component\HttpKernel\Debug\ExceptionHandler;
ExceptionHandler::register();
In production you may want to disable the debug output by passing ``false`` as
the ``$debug`` argument::
use Symfony\Component\HttpKernel\Debug\ExceptionHandler;
ExceptionHandler::register(false);

@ -95,7 +95,7 @@ Before Middleware
A *before* route middleware is fired just before the route callback, but after
the *before* application middlewares::
$before = function (Request $request) use ($app) {
$before = function (Request $request, Application $app) {
// ...
};
@ -110,7 +110,7 @@ After Middleware
An *after* route middleware is fired just after the route callback, but before
the application *after* application middlewares::
$after = function (Request $request, Response $response) use ($app) {
$after = function (Request $request, Response $response, Application $app) {
// ...
};

@ -30,7 +30,7 @@ it can be used for functional tests too. You write tests by creating a new
class, that extends the ``PHPUnit_Framework_TestCase``. Your test cases are
methods prefixed with ``test``::
class ContactFormTest extends PHPUnit_Framework_TestCase
class ContactFormTest extends \PHPUnit_Framework_TestCase
{
public function testInitialPage()
{

@ -462,8 +462,8 @@ the defaults for new controllers.
.. note::
The global configuration does not apply to controller providers you might
mount as they have their own global configuration (see the Modularity
paragraph below).
mount as they have their own global configuration (read the
:doc:`dedicated chapter<organizing_controllers>` for more information).
Error handlers
--------------

@ -43,7 +43,7 @@ use Silex\EventListener\StringToResponseListener;
*/
class Application extends \Pimple implements HttpKernelInterface, TerminableInterface
{
const VERSION = '1.0.0';
const VERSION = '1.0.1';
const EARLY_EVENT = 512;
const LATE_EVENT = -512;

@ -74,7 +74,9 @@ class DoctrineServiceProvider implements ServiceProviderInterface
$manager = $app['dbs.event_manager'][$name];
}
$dbs[$name] = DriverManager::getConnection($options, $config, $manager);
$dbs[$name] = $dbs->share(function ($dbs) use ($options, $config, $manager) {
return DriverManager::getConnection($options, $config, $manager);
});
}
return $dbs;

@ -54,6 +54,7 @@ class RedirectableUrlMatcher extends BaseRedirectableUrlMatcher
return array(
'_controller' => function ($url) { return new RedirectResponse($url, 301); },
'_route' => null,
'url' => $url,
);
}

@ -31,7 +31,7 @@ interface ServiceProviderInterface
/**
* Bootstraps the application.
*
* This method is called after all services are registers
* This method is called after all services are registered
* and should be used for "dynamic" configuration (whenever
* a service must be requested).
*/

@ -14,9 +14,11 @@ namespace Silex\Tests;
use Silex\Application;
use Silex\ControllerCollection;
use Silex\Route;
use Silex\Provider\MonologServiceProvider;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Debug\ErrorHandler;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
@ -500,6 +502,19 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$this->assertFalse(class_exists('Symfony\Component\HttpFoundation\BinaryFileResponse'));
}
}
public function testRedirectDoesNotRaisePHPNoticesWhenMonologIsRegistered()
{
$app = new Application();
ErrorHandler::register();
$app['monolog.logfile'] = 'php://memory';
$app->register(new MonologServiceProvider());
$app->get('/foo/', function() { return 'ok'; });
$response = $app->handle(Request::create('/foo'));
$this->assertEquals(301, $response->getStatusCode());
}
}
class FooController

@ -87,9 +87,8 @@ class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase
));
$builder = $app['form.factory']->createBuilder('form', array(), array(
'validation_constraint' => $constraints, // symfony/validator >=2.1,<2.3
'constraints' => $constraints, // symfony/validator ~2.3
'csrf_protection' => false,
'constraints' => $constraints,
'csrf_protection' => false,
));
$form = $builder

@ -104,6 +104,7 @@ class ErrorHandler
$stack = array_map(
function ($row) {
unset($row['args']);
return $row;
},
array_slice(debug_backtrace(false), 0, 10)

@ -19,18 +19,18 @@ namespace Symfony\Component\Debug\Exception;
class ContextErrorException extends \ErrorException
{
private $context = array();
public function __construct($message, $code, $severity, $filename, $lineno, $context = array())
{
parent::__construct($message, $code, $severity, $filename, $lineno);
$this->context = $context;
}
/**
* @return array Array of variables that existed when the exception occured
*/
public function getContext()
{
return $this->context;
}
}
}

@ -82,6 +82,10 @@ class Crawler extends \SplObjectStorage
/**
* Adds HTML/XML content.
*
* If the charset is not set via the content type, it is assumed
* to be ISO-8859-1, which is the default charset defined by the
* HTTP 1.1 specification.
*
* @param string $content A string to parse as HTML/XML
* @param null|string $type The content type of the string
*

@ -351,6 +351,7 @@ class Form extends Link implements \ArrayAccess
throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId));
}
$this->node = $form;
return;
}
// we loop until we find a form ancestor

@ -125,7 +125,7 @@ class Link
return preg_replace('#^([^/]*)//.*$#', '$1', $this->currentUri).$uri;
}
$baseUri = preg_replace('#^(.*?//[^/]+)(?:\/.*)?$#', '$1', $this->currentUri);
$baseUri = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $this->currentUri);
// absolute path
if ('/' === $uri[0]) {

@ -122,6 +122,11 @@ class LinkTest extends \PHPUnit_Framework_TestCase
array('../bar/./../../foo', 'http://localhost/bar/foo/', 'http://localhost/foo'),
array('../../', 'http://localhost/', 'http://localhost/'),
array('../../', 'http://localhost', 'http://localhost/'),
array('/foo', 'file:///', 'file:///foo'),
array('/foo', 'file:///bar/baz', 'file:///foo'),
array('foo', 'file:///', 'file:///foo'),
array('foo', 'file:///bar/baz', 'file:///bar/foo'),
);
}
}

@ -88,7 +88,6 @@ abstract class RealIteratorTestCase extends IteratorTestCase
}
if (is_string($files)) {
return self::$tmpDir . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $files);
}

@ -37,6 +37,16 @@ class Request
protected static $trustedProxies = array();
/**
* @var string[]
*/
protected static $trustedHostPatterns = array();
/**
* @var string[]
*/
protected static $trustedHosts = array();
/**
* Names for headers that can be trusted when
* using trusted proxies.
@ -486,6 +496,32 @@ class Request
return self::$trustedProxies;
}
/**
* Sets a list of trusted host patterns.
*
* You should only list the hosts you manage using regexs.
*
* @param array $hostPatterns A list of trusted host patterns
*/
public static function setTrustedHosts(array $hostPatterns)
{
self::$trustedHostPatterns = array_map(function ($hostPattern) {
return sprintf('{%s}i', str_replace('}', '\\}', $hostPattern));
}, $hostPatterns);
// we need to reset trusted hosts on trusted host patterns change
self::$trustedHosts = array();
}
/**
* Gets the list of trusted host patterns.
*
* @return array An array of trusted host patterns.
*/
public static function getTrustedHosts()
{
return self::$trustedHostPatterns;
}
/**
* Sets the name for trusted headers.
*
@ -1068,6 +1104,24 @@ class Request
throw new \UnexpectedValueException('Invalid Host');
}
if (count(self::$trustedHostPatterns) > 0) {
// to avoid host header injection attacks, you should provide a list of trusted host patterns
if (in_array($host, self::$trustedHosts)) {
return $host;
}
foreach (self::$trustedHostPatterns as $pattern) {
if (preg_match($pattern, $host)) {
self::$trustedHosts[] = $host;
return $host;
}
}
throw new \UnexpectedValueException('Untrusted Host');
}
return $host;
}

@ -155,7 +155,6 @@ class RequestMatcher implements RequestMatcherInterface
// Note to future implementors: add additional checks above the
// foreach above or else your check might not be run!
return count($this->ips) === 0;
}
}

@ -29,11 +29,6 @@ class MockFileSessionStorage extends MockArraySessionStorage
*/
private $savePath;
/**
* @var array
*/
private $sessionData;
/**
* Constructor.
*

@ -207,7 +207,19 @@ class NativeSessionStorage implements SessionStorageInterface
$this->metadataBag->stampNew();
}
return session_regenerate_id($destroy);
$ret = session_regenerate_id($destroy);
// workaround for https://bugs.php.net/bug.php?id=61470 as suggested by David Grudl
session_write_close();
if (isset($_SESSION)) {
$backup = $_SESSION;
session_start();
$_SESSION = $backup;
} else {
session_start();
}
return $ret;
}
/**

@ -1501,6 +1501,37 @@ class RequestTest extends \PHPUnit_Framework_TestCase
)
);
}
public function testTrustedHosts()
{
// create a request
$request = Request::create('/');
// no trusted host set -> no host check
$request->headers->set('host', 'evil.com');
$this->assertEquals('evil.com', $request->getHost());
// add a trusted domain and all its subdomains
Request::setTrustedHosts(array('.*\.?trusted.com$'));
// untrusted host
$request->headers->set('host', 'evil.com');
try {
$request->getHost();
$this->fail('Request::getHost() should throw an exception when host is not trusted.');
} catch (\UnexpectedValueException $e) {
$this->assertEquals('Untrusted Host', $e->getMessage());
}
// trusted hosts
$request->headers->set('host', 'trusted.com');
$this->assertEquals('trusted.com', $request->getHost());
$request->headers->set('host', 'subdomain.trusted.com');
$this->assertEquals('subdomain.trusted.com', $request->getHost());
// reset request for following tests
Request::setTrustedHosts(array());
}
}
class RequestContentProxy extends Request

@ -13,11 +13,8 @@ namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;

@ -11,7 +11,6 @@
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;

@ -78,6 +78,10 @@ class TimeDataCollector extends DataCollector
*/
public function getDuration()
{
if (!isset($this->data['events']['__section__'])) {
return 0;
}
$lastEvent = $this->data['events']['__section__'];
return $lastEvent->getOrigin() + $lastEvent->getDuration() - $this->getStartTime();
@ -92,6 +96,10 @@ class TimeDataCollector extends DataCollector
*/
public function getInitTime()
{
if (!isset($this->data['events']['__section__'])) {
return 0;
}
return $this->data['events']['__section__']->getOrigin() - $this->getStartTime();
}

@ -32,7 +32,7 @@ abstract class ConfigurableExtension extends Extension
*/
final public function load(array $configs, ContainerBuilder $container)
{
$this->loadInternal($this->processConfiguration($this->getConfiguration(array(), $container), $configs), $container);
$this->loadInternal($this->processConfiguration($this->getConfiguration($configs, $container), $configs), $container);
}
/**

@ -12,7 +12,6 @@
namespace Symfony\Component\HttpKernel\DependencyInjection;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;

@ -11,7 +11,6 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

@ -110,7 +110,7 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
}
$renderedAttributes = '';
if (count($attributes) > 0) {
foreach($attributes as $attribute => $value) {
foreach ($attributes as $attribute => $value) {
$renderedAttributes .= sprintf(
' %s="%s"',
htmlspecialchars($attribute, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset, false),
@ -130,7 +130,11 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
private function templateExists($template)
{
if ($this->templating instanceof EngineInterface) {
return $this->templating->exists($template);
try {
return $this->templating->exists($template);
} catch (\InvalidArgumentException $e) {
return false;
}
}
$loader = $this->templating->getLoader();

@ -52,7 +52,15 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
$reference = null;
if ($uri instanceof ControllerReference) {
$reference = $uri;
// Remove attributes from the genereated URI because if not, the Symfony
// routing system will use them to populate the Request attributes. We don't
// want that as we want to preserve objects (so we manually set Request attributes
// below instead)
$attributes = $reference->attributes;
$reference->attributes = array();
$uri = $this->generateFragmentUri($uri, $request);
$reference->attributes = array_merge($attributes, $reference->attributes);
}
$subRequest = $this->createSubRequest($uri, $request);

@ -52,7 +52,7 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
/**
* {@inheritdoc}
*
*
* @api
*/
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)

@ -61,11 +61,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $classes;
protected $loadClassCache;
const VERSION = '2.3.1';
const VERSION_ID = '20301';
const VERSION = '2.3.3';
const VERSION_ID = '20303';
const MAJOR_VERSION = '2';
const MINOR_VERSION = '3';
const RELEASE_VERSION = '1';
const RELEASE_VERSION = '3';
const EXTRA_VERSION = '';
/**

@ -16,9 +16,6 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Stopwatch\Stopwatch;
@ -194,7 +191,6 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
->method('isStarted')
->will($this->returnValue(false));
$dispatcher = new TraceableEventDispatcher(new EventDispatcher(), $stopwatch);
$kernel = $this->getHttpKernel($dispatcher, function () { return new Response(); });

@ -50,7 +50,7 @@ class HIncludeFragmentRendererTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('<hx:include src="/foo"></hx:include>', $strategy->render('/foo', Request::create('/'))->getContent());
}
public function testRenderWhithDefault()
public function testRenderWithDefault()
{
// only default
$strategy = new HIncludeFragmentRenderer();
@ -79,4 +79,17 @@ class HIncludeFragmentRendererTest extends \PHPUnit_Framework_TestCase
$strategy = new HIncludeFragmentRenderer();
$this->assertEquals('<hx:include src="/foo" p1="v1" p2="v2" id="bar">default</hx:include>', $strategy->render('/foo', Request::create('/'), array('default' => 'default', 'id' => 'bar', 'attributes' => array('p1' => 'v1', 'p2' => 'v2')))->getContent());
}
public function testRenderWithDefaultText()
{
$engine = $this->getMock('Symfony\\Component\\Templating\\EngineInterface');
$engine->expects($this->once())
->method('exists')
->with('default')
->will($this->throwException(new \InvalidArgumentException()));
// only default
$strategy = new HIncludeFragmentRenderer($engine);
$this->assertEquals('<hx:include src="/foo">default</hx:include>', $strategy->render('/foo', Request::create('/'), array('default' => 'default'))->getContent());
}
}

@ -51,11 +51,7 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
$object = new \stdClass();
$subRequest = Request::create('/_fragment?_path=_format%3Dhtml%26_controller%3Dmain_controller');
$subRequest->attributes->replace(array(
'object' => $object,
'_format' => 'html',
'_controller' => 'main_controller',
));
$subRequest->attributes->replace(array('object' => $object, '_format' => 'html', '_controller' => 'main_controller'));
$subRequest->headers->set('x-forwarded-for', array('127.0.0.1'));
$subRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');

@ -23,15 +23,16 @@
"psr/log": "~1.0"
},
"require-dev": {
"symfony/browser-kit": "2.2.*",
"symfony/browser-kit": "~2.2",
"symfony/class-loader": "~2.1",
"symfony/config": "~2.0",
"symfony/console": "2.2.*",
"symfony/console": "~2.2",
"symfony/dependency-injection": "~2.0",
"symfony/finder": "~2.0",
"symfony/process": "~2.0",
"symfony/routing": "~2.2",
"symfony/stopwatch": "~2.2"
"symfony/stopwatch": "~2.2",
"symfony/templating": "~2.2"
},
"suggest": {
"symfony/browser-kit": "",

@ -34,7 +34,7 @@ class PhpExecutableFinder
public function find()
{
// PHP_BINARY return the current sapi executable
if (defined('PHP_BINARY') && PHP_BINARY && ('cli' === PHP_SAPI)) {
if (defined('PHP_BINARY') && PHP_BINARY && ('cli' === PHP_SAPI) && is_file(PHP_BINARY)) {
return PHP_BINARY;
}

@ -147,10 +147,7 @@ class Process
$this->cwd = getcwd();
}
if (null !== $env) {
$this->env = array();
foreach ($env as $key => $value) {
$this->env[(binary) $key] = (binary) $value;
}
$this->setEnv($env);
} else {
$this->env = null;
}
@ -262,9 +259,9 @@ class Process
stream_set_blocking($pipe, false);
}
if ($this->tty) {
$this->status = self::STATUS_TERMINATED;
return;
}
@ -289,15 +286,17 @@ class Process
$w = $writePipes;
$e = null;
$n = @stream_select($r, $w, $e, 0, ceil(static::TIMEOUT_PRECISION * 1E6));
if (false === $n) {
if (false === $n = @stream_select($r, $w, $e, 0, ceil(static::TIMEOUT_PRECISION * 1E6))) {
// if a system call has been interrupted, forget about it, let's try again
if ($this->hasSystemCallBeenInterrupted()) {
continue;
}
break;
}
if ($n === 0) {
proc_terminate($this->process);
throw new RuntimeException('The process timed out.');
// nothing has changed, let's wait until the process is ready
if (0 === $n) {
continue;
}
if ($w) {
@ -387,10 +386,9 @@ class Process
// let's have a look if something changed in streams
if (false === $n = @stream_select($r, $w, $e, 0, ceil(static::TIMEOUT_PRECISION * 1E6))) {
$lastError = error_get_last();
// stream_select returns false when the `select` system call is interrupted by an incoming signal
if (isset($lastError['message']) && false === stripos($lastError['message'], 'interrupted system call')) {
// if a system call has been interrupted, forget about it, let's try again
// otherwise, an error occured, let's reset pipes
if (!$this->hasSystemCallBeenInterrupted()) {
$this->pipes = array();
}
@ -942,13 +940,25 @@ class Process
/**
* Sets the environment variables.
*
* An environment variable value should be a string.
* If it is an array, the variable is ignored.
*
* That happens in PHP when 'argv' is registered into
* the $_ENV array for instance.
*
* @param array $env The new environment variables
*
* @return self The current Process instance
*/
public function setEnv(array $env)
{
$this->env = $env;
// Process can not handle env values that are arrays
$env = array_filter($env, function ($value) { if (!is_array($value)) { return true; } });
$this->env = array();
foreach ($env as $key => $value) {
$this->env[(binary) $key] = (binary) $value;
}
return $this;
}
@ -1092,9 +1102,9 @@ class Process
$this->readBytes = array(
self::STDOUT => 0,
);
return array(array('pipe', 'r'), $this->fileHandles[self::STDOUT], array('pipe', 'w'));
}
}
if ($this->tty) {
$descriptors = array(
@ -1230,4 +1240,17 @@ class Process
}
}
}
/**
* Returns true if a system call has been interrupted.
*
* @return Boolean
*/
private function hasSystemCallBeenInterrupted()
{
$lastError = error_get_last();
// stream_select returns false when the `select` system call is interrupted by an incoming signal
return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call');
}
}

@ -42,7 +42,7 @@ class ProcessUtils
//@see https://bugs.php.net/bug.php?id=49446
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$escapedArgument = '';
foreach(preg_split('/([%"])/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
foreach (preg_split('/([%"])/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
if ('"' == $part) {
$escapedArgument .= '\\"';
} elseif ('%' == $part) {

@ -47,9 +47,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testStopWithTimeoutIsActuallyWorking()
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->markTestSkipped('Stop with timeout does not work on windows, it requires posix signals');
}
$this->verifyPosixIsEnabled();
// exec is mandatory here since we send a signal to the process
// see https://github.com/symfony/symfony/issues/5030 about prepending
@ -64,7 +62,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
}
$duration = microtime(true) - $start;
$this->assertLessThan(1.3, $duration);
$this->assertLessThan(1.8, $duration);
}
/**
@ -454,9 +452,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testSignal()
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->markTestSkipped('POSIX signals do not work on windows');
}
$this->verifyPosixIsEnabled();
$process = $this->getProcess('exec php -f ' . __DIR__ . '/SignalListener.php');
$process->start();
@ -474,13 +470,20 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
* @expectedException Symfony\Component\Process\Exception\LogicException
*/
public function testSignalProcessNotRunning()
{
$this->verifyPosixIsEnabled();
$process = $this->getProcess('php -m');
$process->signal(SIGHUP);
}
private function verifyPosixIsEnabled()
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->markTestSkipped('POSIX signals do not work on windows');
}
$process = $this->getProcess('php -m');
$process->signal(SIGHUP);
if (!defined('SIGUSR1')) {
$this->markTestSkipped('The pcntl extension is not enabled');
}
}
/**

@ -45,6 +45,22 @@ class ProcessBuilderTest extends \PHPUnit_Framework_TestCase
$_ENV = $snapshot;
}
public function testProcessBuilderShouldNotPassEnvArrays()
{
$snapshot = $_ENV;
$_ENV = array('a' => array('b', 'c'), 'd' => 'e', 'f' => 'g');
$expected = array('d' => 'e', 'f' => 'g');
$pb = new ProcessBuilder();
$pb->add('a')->inheritEnvironmentVariables()
->setEnv('d', 'e');
$proc = $pb->getProcess();
$this->assertEquals($expected, $proc->getEnv(), '->inheritEnvironmentVariables() removes array values from $_ENV');
$_ENV = $snapshot;
}
public function testInheritEnvironmentVarsByDefault()
{
$pb = new ProcessBuilder();

@ -8,9 +8,9 @@ pcntl_signal(SIGUSR1, function(){echo "Caught SIGUSR1"; exit;});
$n=0;
// ticks require activity to work - sleep(4); does not work
while($n < 400) {
while ($n < 400) {
usleep(10000);
$n++;
}
return;
return;

@ -141,7 +141,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
}
/**
* @throws MissingMandatoryParametersException When some parameters are missing that mandatory for the route
* @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route
* @throws InvalidParameterException When a parameter value for a placeholder is not correct because
* it does not match the requirement
*/

@ -39,7 +39,7 @@ class ApacheUrlMatcher extends UrlMatcher
$allow = array();
$route = null;
foreach ($_SERVER as $key => $value) {
foreach ($this->denormalizeValues($_SERVER) as $key => $value) {
$name = $key;
// skip non-routing variables
@ -91,4 +91,28 @@ class ApacheUrlMatcher extends UrlMatcher
return parent::match($pathinfo);
}
}
/**
* Denormalizes an array of values.
*
* @param string[] $values
*
* @return array
*/
private function denormalizeValues(array $values)
{
$normalizedValues = array();
foreach ($values as $key => $value) {
if (preg_match('~^(.*)\[(\d+)\]$~', $key, $matches)) {
if (!isset($normalizedValues[$matches[1]])) {
$normalizedValues[$matches[1]] = array();
}
$normalizedValues[$matches[1]][(int) $matches[2]] = $value;
} else {
$normalizedValues[$key] = $value;
}
}
return $normalizedValues;
}
}

@ -132,7 +132,7 @@ class ApacheMatcherDumper extends MatcherDumper
foreach ($compiledRoute->getPathVariables() as $i => $variable) {
$variables[] = 'E=_ROUTING_param_'.$variable.':%'.($i + 1);
}
foreach ($route->getDefaults() as $key => $value) {
foreach ($this->normalizeValues($route->getDefaults()) as $key => $value) {
$variables[] = 'E=_ROUTING_default_'.$key.':'.strtr($value, array(
':' => '\\:',
'=' => '\\=',
@ -151,7 +151,7 @@ class ApacheMatcherDumper extends MatcherDumper
$allow[] = 'E=_ROUTING_allow_'.$method.':1';
}
if ($hostRegex = $compiledRoute->getHostRegex()) {
if ($compiledRoute->getHostRegex()) {
$rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_host_%s} =1", $hostRegexUnique);
}
@ -162,8 +162,7 @@ class ApacheMatcherDumper extends MatcherDumper
// redirect with trailing slash appended
if ($hasTrailingSlash) {
if ($hostRegex = $compiledRoute->getHostRegex()) {
if ($compiledRoute->getHostRegex()) {
$rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_host_%s} =1", $hostRegexUnique);
}
@ -173,7 +172,7 @@ class ApacheMatcherDumper extends MatcherDumper
// the main rule
if ($hostRegex = $compiledRoute->getHostRegex()) {
if ($compiledRoute->getHostRegex()) {
$rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_host_%s} =1", $hostRegexUnique);
}
@ -249,4 +248,27 @@ class ApacheMatcherDumper extends MatcherDumper
return $output;
}
/**
* Normalizes an array of values.
*
* @param array $values
*
* @return string[]
*/
private function normalizeValues(array $values)
{
$normalizedValues = array();
foreach ($values as $key => $value) {
if (is_array($value)) {
foreach ($value as $index => $bit) {
$normalizedValues[sprintf('%s[%s]', $key, $index)] = $bit;
}
} else {
$normalizedValues[$key] = (string) $value;
}
}
return $normalizedValues;
}
}

@ -90,6 +90,21 @@ class ApacheUrlMatcherTest extends \PHPUnit_Framework_TestCase
'_route' => 'hello',
),
),
array(
'Redirect with many ignored attributes',
'/legacy/{cat1}/{cat2}/{id}.html',
array(
'_ROUTING_route' => 'product_view',
'_ROUTING_param__controller' => 'FrameworkBundle:Redirect:redirect',
'_ROUTING_default_ignoreAttributes[0]' => 'attr_a',
'_ROUTING_default_ignoreAttributes[1]' => 'attr_b',
),
array(
'ignoreAttributes' => array('attr_a', 'attr_b'),
'_controller' => 'FrameworkBundle:Redirect:redirect',
'_route' => 'product_view',
)
),
array(
'REDIRECT_ envs',
'/hello/world',
@ -131,7 +146,7 @@ class ApacheUrlMatcherTest extends \PHPUnit_Framework_TestCase
'name' => 'world',
'_route' => 'hello',
),
),
)
);
}
}

Loading…
Cancel
Save