├── .gitignore ├── README.md ├── app ├── .htaccess ├── AppCache.php ├── AppKernel.php ├── Resources │ └── views │ │ ├── base.html.twig │ │ └── default │ │ └── index.html.twig ├── SymfonyRequirements.php ├── autoload.php ├── cache │ └── .gitkeep ├── check.php ├── config │ ├── config.yml │ ├── config_dev.yml │ ├── config_prod.yml │ ├── config_test.yml │ ├── parameters.yml.dist │ ├── routing.yml │ ├── routing_dev.yml │ ├── security.yml │ └── services.yml ├── console ├── logs │ └── .gitkeep └── phpunit.xml.dist ├── composer.json ├── composer.lock ├── screenshot.png ├── src ├── .htaccess ├── AppBundle │ ├── AppBundle.php │ ├── Collector │ │ └── MultiDbCollector.php │ ├── Connection │ │ └── Wrapper.php │ ├── Controller │ │ └── DefaultController.php │ ├── DataFixtures │ │ ├── Standard │ │ │ └── LoadTenantsData.php │ │ └── Tenant │ │ │ └── LoadInvoicesData.php │ ├── Entity │ │ ├── Tenant.php │ │ └── TenantRepository.php │ ├── Listener │ │ ├── CommandListener.php │ │ ├── DatabaseListener.php │ │ ├── FixturesListener.php │ │ └── TenantListener.php │ ├── Resources │ │ ├── config │ │ │ └── doctrine │ │ │ │ ├── App │ │ │ │ └── Tenant.orm.yml │ │ │ │ └── Client │ │ │ │ └── Invoice.orm.yml │ │ └── views │ │ │ └── Toolbar │ │ │ └── DatabaseConnections.html.twig │ ├── TenantProviderInterface.php │ └── Tests │ │ └── Controller │ │ └── DefaultControllerTest.php └── Client │ ├── Invoice.php │ └── InvoiceRepository.php └── web ├── .htaccess ├── app.php ├── app_dev.php ├── apple-touch-icon.png ├── config.php ├── favicon.ico └── robots.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /web/bundles/ 2 | /app/bootstrap.php.cache 3 | /app/cache/* 4 | /app/config/parameters.yml 5 | /app/logs/* 6 | !app/cache/.gitkeep 7 | !app/logs/.gitkeep 8 | /app/phpunit.xml 9 | /build/ 10 | /vendor/ 11 | /bin/ 12 | /composer.phar 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | multitenant application proof-of-concept 2 | ======================================== 3 | 4 | about: 5 | ------ 6 | 7 | This is a simple application with basic concept of two connections to database: one is used as a default or main, and provides credentials for second connection. Credentials are obtained basing on ID taken from url parameter, but nothing stops us from storing these credentials in config file or any other storage (interfaced with `TenantProviderInterface`). 8 | The Tenant connection (second one) can be switched dynamically to other database. Main idea is to have multiple databases, with same schema and behaviour, but seperated from each other. This can be a optimization or security requirement for some applications. 9 | Tenants can be obtained basing upon different criterias. Most common are: subdomain, user or server parameter. In our example, there's a url query parameter named `db` where we pass a tenant id, for example: `http://localhost:8000/?db=superman` will be captured by listener, that will take from `multidb` a tenant entity with `spiderman` id and use it credentials to connect to specified db. 10 | 11 | installation: 12 | ------------- 13 | 14 | **RUN ONLY ON LOCAL DEVELOPMENT MACHINE, can break your DB!!!** 15 | 16 | 1. checkout project to a directory 17 | 2. `composer install` 18 | 3. edit *app/config/parameters.yml* so database user passed to application has a DBMA privileges (creation and dropping databases) 19 | 4. `composer prepare-default` will create and seed *multidb* database 20 | 5. `composer prepare-tenant` will create and seed three *multidb_* databases. *for visibility of differences, multidb_spiderman is not seeded!* 21 | 6. `php app/console server:start` to start application in web browser 22 | 23 | *** DISCLAIMER: 24 | can explode your computer, I'm not sure. Better don't touch unless you know what are you doing :) *** 25 | 26 | tips: 27 | ----- 28 | 29 | 1. default connection database needs to be populated with some tenants before trying to create db for them 30 | 2. every command defined in parameter 'allowed_commands' (in app/config/services.yml) has a new argument `tenant` which is ID of tenant in db and a suffix of database. you can check list of these commands runnning `sf debug:container --parameter=allowed_commands` 31 | 3. in debug mode with visible web_toolbar there's a small indicator of currently connected databases 32 | 33 | screenshot: 34 | ----------- 35 | 36 | ![screenshot.png](screenshot.png) 37 | -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Require all denied 3 | 4 | 5 | Order deny,allow 6 | Deny from all 7 | 8 | -------------------------------------------------------------------------------- /app/AppCache.php: -------------------------------------------------------------------------------- 1 | getEnvironment(), array('dev', 'test'), true)) { 22 | $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); 23 | $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); 24 | $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); 25 | $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); 26 | } 27 | 28 | return $bundles; 29 | } 30 | 31 | public function registerContainerConfiguration(LoaderInterface $loader) 32 | { 33 | $loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/Resources/views/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block title %}Welcome!{% endblock %} 9 | 10 | {% block stylesheets %}{% endblock %} 11 | 12 | 13 | 14 | 18 | 19 | 20 | {% block body %}{% endblock %} 21 | {% block javascripts %}{% endblock %} 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/Resources/views/default/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | {# @var tenants \AppBundle\Entity\Tenant[] #} 3 | {# @var invoices \Client\Invoice[] #} 4 | 5 | {% block stylesheets %} 6 | 7 | 8 | {% endblock stylesheets %} 9 | 10 | {% block javascripts %} 11 | 12 | {% endblock javascripts %} 13 | 14 | {% block body %} 15 |
16 |
17 | 24 |

Project name

25 |
26 | 27 |
28 |

Multitenant database app!

29 |

Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

30 |

Sign up today

31 |
32 | 33 |
34 |
35 | {% if name is defined %} 36 |

Invoices for {{ name }}:

37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {% for invoice in invoices %} 48 | 49 | 50 | 51 | 52 | 53 | {% endfor %} 54 | 55 |
NumberCompanyCreatedAt
{{ invoice.number }}{{ invoice.company }}{{ invoice.createdAt|date('Y-m-d') }}
56 | 57 | {% endif %} 58 |
59 |
60 |
61 | {% endblock %} 62 | -------------------------------------------------------------------------------- /app/SymfonyRequirements.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | /* 13 | * Users of PHP 5.2 should be able to run the requirements checks. 14 | * This is why the file and all classes must be compatible with PHP 5.2+ 15 | * (e.g. not using namespaces and closures). 16 | * 17 | * ************** CAUTION ************** 18 | * 19 | * DO NOT EDIT THIS FILE as it will be overridden by Composer as part of 20 | * the installation/update process. The original file resides in the 21 | * SensioDistributionBundle. 22 | * 23 | * ************** CAUTION ************** 24 | */ 25 | 26 | /** 27 | * Represents a single PHP requirement, e.g. an installed extension. 28 | * It can be a mandatory requirement or an optional recommendation. 29 | * There is a special subclass, named PhpIniRequirement, to check a php.ini configuration. 30 | * 31 | * @author Tobias Schultze 32 | */ 33 | class Requirement 34 | { 35 | private $fulfilled; 36 | private $testMessage; 37 | private $helpText; 38 | private $helpHtml; 39 | private $optional; 40 | 41 | /** 42 | * Constructor that initializes the requirement. 43 | * 44 | * @param bool $fulfilled Whether the requirement is fulfilled 45 | * @param string $testMessage The message for testing the requirement 46 | * @param string $helpHtml The help text formatted in HTML for resolving the problem 47 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 48 | * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement 49 | */ 50 | public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false) 51 | { 52 | $this->fulfilled = (bool) $fulfilled; 53 | $this->testMessage = (string) $testMessage; 54 | $this->helpHtml = (string) $helpHtml; 55 | $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText; 56 | $this->optional = (bool) $optional; 57 | } 58 | 59 | /** 60 | * Returns whether the requirement is fulfilled. 61 | * 62 | * @return bool true if fulfilled, otherwise false 63 | */ 64 | public function isFulfilled() 65 | { 66 | return $this->fulfilled; 67 | } 68 | 69 | /** 70 | * Returns the message for testing the requirement. 71 | * 72 | * @return string The test message 73 | */ 74 | public function getTestMessage() 75 | { 76 | return $this->testMessage; 77 | } 78 | 79 | /** 80 | * Returns the help text for resolving the problem. 81 | * 82 | * @return string The help text 83 | */ 84 | public function getHelpText() 85 | { 86 | return $this->helpText; 87 | } 88 | 89 | /** 90 | * Returns the help text formatted in HTML. 91 | * 92 | * @return string The HTML help 93 | */ 94 | public function getHelpHtml() 95 | { 96 | return $this->helpHtml; 97 | } 98 | 99 | /** 100 | * Returns whether this is only an optional recommendation and not a mandatory requirement. 101 | * 102 | * @return bool true if optional, false if mandatory 103 | */ 104 | public function isOptional() 105 | { 106 | return $this->optional; 107 | } 108 | } 109 | 110 | /** 111 | * Represents a PHP requirement in form of a php.ini configuration. 112 | * 113 | * @author Tobias Schultze 114 | */ 115 | class PhpIniRequirement extends Requirement 116 | { 117 | /** 118 | * Constructor that initializes the requirement. 119 | * 120 | * @param string $cfgName The configuration name used for ini_get() 121 | * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, 122 | * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement 123 | * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. 124 | * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. 125 | * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. 126 | * @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) 127 | * @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) 128 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 129 | * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement 130 | */ 131 | public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false) 132 | { 133 | $cfgValue = ini_get($cfgName); 134 | 135 | if (is_callable($evaluation)) { 136 | if (null === $testMessage || null === $helpHtml) { 137 | throw new InvalidArgumentException('You must provide the parameters testMessage and helpHtml for a callback evaluation.'); 138 | } 139 | 140 | $fulfilled = call_user_func($evaluation, $cfgValue); 141 | } else { 142 | if (null === $testMessage) { 143 | $testMessage = sprintf('%s %s be %s in php.ini', 144 | $cfgName, 145 | $optional ? 'should' : 'must', 146 | $evaluation ? 'enabled' : 'disabled' 147 | ); 148 | } 149 | 150 | if (null === $helpHtml) { 151 | $helpHtml = sprintf('Set %s to %s in php.ini*.', 152 | $cfgName, 153 | $evaluation ? 'on' : 'off' 154 | ); 155 | } 156 | 157 | $fulfilled = $evaluation == $cfgValue; 158 | } 159 | 160 | parent::__construct($fulfilled || ($approveCfgAbsence && false === $cfgValue), $testMessage, $helpHtml, $helpText, $optional); 161 | } 162 | } 163 | 164 | /** 165 | * A RequirementCollection represents a set of Requirement instances. 166 | * 167 | * @author Tobias Schultze 168 | */ 169 | class RequirementCollection implements IteratorAggregate 170 | { 171 | /** 172 | * @var Requirement[] 173 | */ 174 | private $requirements = array(); 175 | 176 | /** 177 | * Gets the current RequirementCollection as an Iterator. 178 | * 179 | * @return Traversable A Traversable interface 180 | */ 181 | public function getIterator() 182 | { 183 | return new ArrayIterator($this->requirements); 184 | } 185 | 186 | /** 187 | * Adds a Requirement. 188 | * 189 | * @param Requirement $requirement A Requirement instance 190 | */ 191 | public function add(Requirement $requirement) 192 | { 193 | $this->requirements[] = $requirement; 194 | } 195 | 196 | /** 197 | * Adds a mandatory requirement. 198 | * 199 | * @param bool $fulfilled Whether the requirement is fulfilled 200 | * @param string $testMessage The message for testing the requirement 201 | * @param string $helpHtml The help text formatted in HTML for resolving the problem 202 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 203 | */ 204 | public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null) 205 | { 206 | $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, false)); 207 | } 208 | 209 | /** 210 | * Adds an optional recommendation. 211 | * 212 | * @param bool $fulfilled Whether the recommendation is fulfilled 213 | * @param string $testMessage The message for testing the recommendation 214 | * @param string $helpHtml The help text formatted in HTML for resolving the problem 215 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 216 | */ 217 | public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null) 218 | { 219 | $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, true)); 220 | } 221 | 222 | /** 223 | * Adds a mandatory requirement in form of a php.ini configuration. 224 | * 225 | * @param string $cfgName The configuration name used for ini_get() 226 | * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, 227 | * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement 228 | * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. 229 | * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. 230 | * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. 231 | * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) 232 | * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) 233 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 234 | */ 235 | public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) 236 | { 237 | $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, false)); 238 | } 239 | 240 | /** 241 | * Adds an optional recommendation in form of a php.ini configuration. 242 | * 243 | * @param string $cfgName The configuration name used for ini_get() 244 | * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, 245 | * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement 246 | * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. 247 | * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. 248 | * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. 249 | * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) 250 | * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) 251 | * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) 252 | */ 253 | public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) 254 | { 255 | $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, true)); 256 | } 257 | 258 | /** 259 | * Adds a requirement collection to the current set of requirements. 260 | * 261 | * @param RequirementCollection $collection A RequirementCollection instance 262 | */ 263 | public function addCollection(RequirementCollection $collection) 264 | { 265 | $this->requirements = array_merge($this->requirements, $collection->all()); 266 | } 267 | 268 | /** 269 | * Returns both requirements and recommendations. 270 | * 271 | * @return Requirement[] 272 | */ 273 | public function all() 274 | { 275 | return $this->requirements; 276 | } 277 | 278 | /** 279 | * Returns all mandatory requirements. 280 | * 281 | * @return Requirement[] 282 | */ 283 | public function getRequirements() 284 | { 285 | $array = array(); 286 | foreach ($this->requirements as $req) { 287 | if (!$req->isOptional()) { 288 | $array[] = $req; 289 | } 290 | } 291 | 292 | return $array; 293 | } 294 | 295 | /** 296 | * Returns the mandatory requirements that were not met. 297 | * 298 | * @return Requirement[] 299 | */ 300 | public function getFailedRequirements() 301 | { 302 | $array = array(); 303 | foreach ($this->requirements as $req) { 304 | if (!$req->isFulfilled() && !$req->isOptional()) { 305 | $array[] = $req; 306 | } 307 | } 308 | 309 | return $array; 310 | } 311 | 312 | /** 313 | * Returns all optional recommendations. 314 | * 315 | * @return Requirement[] 316 | */ 317 | public function getRecommendations() 318 | { 319 | $array = array(); 320 | foreach ($this->requirements as $req) { 321 | if ($req->isOptional()) { 322 | $array[] = $req; 323 | } 324 | } 325 | 326 | return $array; 327 | } 328 | 329 | /** 330 | * Returns the recommendations that were not met. 331 | * 332 | * @return Requirement[] 333 | */ 334 | public function getFailedRecommendations() 335 | { 336 | $array = array(); 337 | foreach ($this->requirements as $req) { 338 | if (!$req->isFulfilled() && $req->isOptional()) { 339 | $array[] = $req; 340 | } 341 | } 342 | 343 | return $array; 344 | } 345 | 346 | /** 347 | * Returns whether a php.ini configuration is not correct. 348 | * 349 | * @return bool php.ini configuration problem? 350 | */ 351 | public function hasPhpIniConfigIssue() 352 | { 353 | foreach ($this->requirements as $req) { 354 | if (!$req->isFulfilled() && $req instanceof PhpIniRequirement) { 355 | return true; 356 | } 357 | } 358 | 359 | return false; 360 | } 361 | 362 | /** 363 | * Returns the PHP configuration file (php.ini) path. 364 | * 365 | * @return string|false php.ini file path 366 | */ 367 | public function getPhpIniConfigPath() 368 | { 369 | return get_cfg_var('cfg_file_path'); 370 | } 371 | } 372 | 373 | /** 374 | * This class specifies all requirements and optional recommendations that 375 | * are necessary to run the Symfony Standard Edition. 376 | * 377 | * @author Tobias Schultze 378 | * @author Fabien Potencier 379 | */ 380 | class SymfonyRequirements extends RequirementCollection 381 | { 382 | const LEGACY_REQUIRED_PHP_VERSION = '5.3.3'; 383 | const REQUIRED_PHP_VERSION = '5.5.9'; 384 | 385 | /** 386 | * Constructor that initializes the requirements. 387 | */ 388 | public function __construct() 389 | { 390 | /* mandatory requirements follow */ 391 | 392 | $installedPhpVersion = phpversion(); 393 | $requiredPhpVersion = $this->getPhpRequiredVersion(); 394 | 395 | $this->addRecommendation( 396 | $requiredPhpVersion, 397 | 'Vendors should be installed in order to check all requirements.', 398 | 'Run the composer install command.', 399 | 'Run the "composer install" command.' 400 | ); 401 | 402 | if (false !== $requiredPhpVersion) { 403 | $this->addRequirement( 404 | version_compare($installedPhpVersion, $requiredPhpVersion, '>='), 405 | sprintf('PHP version must be at least %s (%s installed)', $requiredPhpVersion, $installedPhpVersion), 406 | sprintf('You are running PHP version "%s", but Symfony needs at least PHP "%s" to run. 407 | Before using Symfony, upgrade your PHP installation, preferably to the latest version.', 408 | $installedPhpVersion, $requiredPhpVersion), 409 | sprintf('Install PHP %s or newer (installed version is %s)', $requiredPhpVersion, $installedPhpVersion) 410 | ); 411 | } 412 | 413 | $this->addRequirement( 414 | version_compare($installedPhpVersion, '5.3.16', '!='), 415 | 'PHP version must not be 5.3.16 as Symfony won\'t work properly with it', 416 | 'Install PHP 5.3.17 or newer (or downgrade to an earlier PHP version)' 417 | ); 418 | 419 | $this->addRequirement( 420 | is_dir(__DIR__.'/../vendor/composer'), 421 | 'Vendor libraries must be installed', 422 | 'Vendor libraries are missing. Install composer following instructions from http://getcomposer.org/. '. 423 | 'Then run "php composer.phar install" to install them.' 424 | ); 425 | 426 | $cacheDir = is_dir(__DIR__.'/../var/cache') ? __DIR__.'/../var/cache' : __DIR__.'/cache'; 427 | 428 | $this->addRequirement( 429 | is_writable($cacheDir), 430 | 'app/cache/ or var/cache/ directory must be writable', 431 | 'Change the permissions of either "app/cache/" or "var/cache/" directory so that the web server can write into it.' 432 | ); 433 | 434 | $logsDir = is_dir(__DIR__.'/../var/logs') ? __DIR__.'/../var/logs' : __DIR__.'/logs'; 435 | 436 | $this->addRequirement( 437 | is_writable($logsDir), 438 | 'app/logs/ or var/logs/ directory must be writable', 439 | 'Change the permissions of either "app/logs/" or "var/logs/" directory so that the web server can write into it.' 440 | ); 441 | 442 | if (version_compare($installedPhpVersion, '7.0.0', '<')) { 443 | $this->addPhpIniRequirement( 444 | 'date.timezone', true, false, 445 | 'date.timezone setting must be set', 446 | 'Set the "date.timezone" setting in php.ini* (like Europe/Paris).' 447 | ); 448 | } 449 | 450 | if (false !== $requiredPhpVersion && version_compare($installedPhpVersion, $requiredPhpVersion, '>=')) { 451 | $timezones = array(); 452 | foreach (DateTimeZone::listAbbreviations() as $abbreviations) { 453 | foreach ($abbreviations as $abbreviation) { 454 | $timezones[$abbreviation['timezone_id']] = true; 455 | } 456 | } 457 | 458 | $this->addRequirement( 459 | isset($timezones[@date_default_timezone_get()]), 460 | sprintf('Configured default timezone "%s" must be supported by your installation of PHP', @date_default_timezone_get()), 461 | 'Your default timezone is not supported by PHP. Check for typos in your php.ini file and have a look at the list of deprecated timezones at http://php.net/manual/en/timezones.others.php.' 462 | ); 463 | } 464 | 465 | $this->addRequirement( 466 | function_exists('iconv'), 467 | 'iconv() must be available', 468 | 'Install and enable the iconv extension.' 469 | ); 470 | 471 | $this->addRequirement( 472 | function_exists('json_encode'), 473 | 'json_encode() must be available', 474 | 'Install and enable the JSON extension.' 475 | ); 476 | 477 | $this->addRequirement( 478 | function_exists('session_start'), 479 | 'session_start() must be available', 480 | 'Install and enable the session extension.' 481 | ); 482 | 483 | $this->addRequirement( 484 | function_exists('ctype_alpha'), 485 | 'ctype_alpha() must be available', 486 | 'Install and enable the ctype extension.' 487 | ); 488 | 489 | $this->addRequirement( 490 | function_exists('token_get_all'), 491 | 'token_get_all() must be available', 492 | 'Install and enable the Tokenizer extension.' 493 | ); 494 | 495 | $this->addRequirement( 496 | function_exists('simplexml_import_dom'), 497 | 'simplexml_import_dom() must be available', 498 | 'Install and enable the SimpleXML extension.' 499 | ); 500 | 501 | if (function_exists('apc_store') && ini_get('apc.enabled')) { 502 | if (version_compare($installedPhpVersion, '5.4.0', '>=')) { 503 | $this->addRequirement( 504 | version_compare(phpversion('apc'), '3.1.13', '>='), 505 | 'APC version must be at least 3.1.13 when using PHP 5.4', 506 | 'Upgrade your APC extension (3.1.13+).' 507 | ); 508 | } else { 509 | $this->addRequirement( 510 | version_compare(phpversion('apc'), '3.0.17', '>='), 511 | 'APC version must be at least 3.0.17', 512 | 'Upgrade your APC extension (3.0.17+).' 513 | ); 514 | } 515 | } 516 | 517 | $this->addPhpIniRequirement('detect_unicode', false); 518 | 519 | if (extension_loaded('suhosin')) { 520 | $this->addPhpIniRequirement( 521 | 'suhosin.executor.include.whitelist', 522 | create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'), 523 | false, 524 | 'suhosin.executor.include.whitelist must be configured correctly in php.ini', 525 | 'Add "phar" to suhosin.executor.include.whitelist in php.ini*.' 526 | ); 527 | } 528 | 529 | if (extension_loaded('xdebug')) { 530 | $this->addPhpIniRequirement( 531 | 'xdebug.show_exception_trace', false, true 532 | ); 533 | 534 | $this->addPhpIniRequirement( 535 | 'xdebug.scream', false, true 536 | ); 537 | 538 | $this->addPhpIniRecommendation( 539 | 'xdebug.max_nesting_level', 540 | create_function('$cfgValue', 'return $cfgValue > 100;'), 541 | true, 542 | 'xdebug.max_nesting_level should be above 100 in php.ini', 543 | 'Set "xdebug.max_nesting_level" to e.g. "250" in php.ini* to stop Xdebug\'s infinite recursion protection erroneously throwing a fatal error in your project.' 544 | ); 545 | } 546 | 547 | $pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null; 548 | 549 | $this->addRequirement( 550 | null !== $pcreVersion, 551 | 'PCRE extension must be available', 552 | 'Install the PCRE extension (version 8.0+).' 553 | ); 554 | 555 | if (extension_loaded('mbstring')) { 556 | $this->addPhpIniRequirement( 557 | 'mbstring.func_overload', 558 | create_function('$cfgValue', 'return (int) $cfgValue === 0;'), 559 | true, 560 | 'string functions should not be overloaded', 561 | 'Set "mbstring.func_overload" to 0 in php.ini* to disable function overloading by the mbstring extension.' 562 | ); 563 | } 564 | 565 | /* optional recommendations follow */ 566 | 567 | if (file_exists(__DIR__.'/../vendor/composer')) { 568 | require_once __DIR__.'/../vendor/autoload.php'; 569 | 570 | try { 571 | $r = new ReflectionClass('Sensio\Bundle\DistributionBundle\SensioDistributionBundle'); 572 | 573 | $contents = file_get_contents(dirname($r->getFileName()).'/Resources/skeleton/app/SymfonyRequirements.php'); 574 | } catch (ReflectionException $e) { 575 | $contents = ''; 576 | } 577 | $this->addRecommendation( 578 | file_get_contents(__FILE__) === $contents, 579 | 'Requirements file should be up-to-date', 580 | 'Your requirements file is outdated. Run composer install and re-check your configuration.' 581 | ); 582 | } 583 | 584 | $this->addRecommendation( 585 | version_compare($installedPhpVersion, '5.3.4', '>='), 586 | 'You should use at least PHP 5.3.4 due to PHP bug #52083 in earlier versions', 587 | 'Your project might malfunction randomly due to PHP bug #52083 ("Notice: Trying to get property of non-object"). Install PHP 5.3.4 or newer.' 588 | ); 589 | 590 | $this->addRecommendation( 591 | version_compare($installedPhpVersion, '5.3.8', '>='), 592 | 'When using annotations you should have at least PHP 5.3.8 due to PHP bug #55156', 593 | 'Install PHP 5.3.8 or newer if your project uses annotations.' 594 | ); 595 | 596 | $this->addRecommendation( 597 | version_compare($installedPhpVersion, '5.4.0', '!='), 598 | 'You should not use PHP 5.4.0 due to the PHP bug #61453', 599 | 'Your project might not work properly due to the PHP bug #61453 ("Cannot dump definitions which have method calls"). Install PHP 5.4.1 or newer.' 600 | ); 601 | 602 | $this->addRecommendation( 603 | version_compare($installedPhpVersion, '5.4.11', '>='), 604 | 'When using the logout handler from the Symfony Security Component, you should have at least PHP 5.4.11 due to PHP bug #63379 (as a workaround, you can also set invalidate_session to false in the security logout handler configuration)', 605 | 'Install PHP 5.4.11 or newer if your project uses the logout handler from the Symfony Security Component.' 606 | ); 607 | 608 | $this->addRecommendation( 609 | (version_compare($installedPhpVersion, '5.3.18', '>=') && version_compare($installedPhpVersion, '5.4.0', '<')) 610 | || 611 | version_compare($installedPhpVersion, '5.4.8', '>='), 612 | 'You should use PHP 5.3.18+ or PHP 5.4.8+ to always get nice error messages for fatal errors in the development environment due to PHP bug #61767/#60909', 613 | 'Install PHP 5.3.18+ or PHP 5.4.8+ if you want nice error messages for all fatal errors in the development environment.' 614 | ); 615 | 616 | if (null !== $pcreVersion) { 617 | $this->addRecommendation( 618 | $pcreVersion >= 8.0, 619 | sprintf('PCRE extension should be at least version 8.0 (%s installed)', $pcreVersion), 620 | 'PCRE 8.0+ is preconfigured in PHP since 5.3.2 but you are using an outdated version of it. Symfony probably works anyway but it is recommended to upgrade your PCRE extension.' 621 | ); 622 | } 623 | 624 | $this->addRecommendation( 625 | class_exists('DomDocument'), 626 | 'PHP-DOM and PHP-XML modules should be installed', 627 | 'Install and enable the PHP-DOM and the PHP-XML modules.' 628 | ); 629 | 630 | $this->addRecommendation( 631 | function_exists('mb_strlen'), 632 | 'mb_strlen() should be available', 633 | 'Install and enable the mbstring extension.' 634 | ); 635 | 636 | $this->addRecommendation( 637 | function_exists('iconv'), 638 | 'iconv() should be available', 639 | 'Install and enable the iconv extension.' 640 | ); 641 | 642 | $this->addRecommendation( 643 | function_exists('utf8_decode'), 644 | 'utf8_decode() should be available', 645 | 'Install and enable the XML extension.' 646 | ); 647 | 648 | $this->addRecommendation( 649 | function_exists('filter_var'), 650 | 'filter_var() should be available', 651 | 'Install and enable the filter extension.' 652 | ); 653 | 654 | if (!defined('PHP_WINDOWS_VERSION_BUILD')) { 655 | $this->addRecommendation( 656 | function_exists('posix_isatty'), 657 | 'posix_isatty() should be available', 658 | 'Install and enable the php_posix extension (used to colorize the CLI output).' 659 | ); 660 | } 661 | 662 | $this->addRecommendation( 663 | extension_loaded('intl'), 664 | 'intl extension should be available', 665 | 'Install and enable the intl extension (used for validators).' 666 | ); 667 | 668 | if (extension_loaded('intl')) { 669 | // in some WAMP server installations, new Collator() returns null 670 | $this->addRecommendation( 671 | null !== new Collator('fr_FR'), 672 | 'intl extension should be correctly configured', 673 | 'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.' 674 | ); 675 | 676 | // check for compatible ICU versions (only done when you have the intl extension) 677 | if (defined('INTL_ICU_VERSION')) { 678 | $version = INTL_ICU_VERSION; 679 | } else { 680 | $reflector = new ReflectionExtension('intl'); 681 | 682 | ob_start(); 683 | $reflector->info(); 684 | $output = strip_tags(ob_get_clean()); 685 | 686 | preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches); 687 | $version = $matches[1]; 688 | } 689 | 690 | $this->addRecommendation( 691 | version_compare($version, '4.0', '>='), 692 | 'intl ICU version should be at least 4+', 693 | 'Upgrade your intl extension with a newer ICU version (4+).' 694 | ); 695 | 696 | if (class_exists('Symfony\Component\Intl\Intl')) { 697 | $this->addRecommendation( 698 | \Symfony\Component\Intl\Intl::getIcuDataVersion() <= \Symfony\Component\Intl\Intl::getIcuVersion(), 699 | sprintf('intl ICU version installed on your system is outdated (%s) and does not match the ICU data bundled with Symfony (%s)', \Symfony\Component\Intl\Intl::getIcuVersion(), \Symfony\Component\Intl\Intl::getIcuDataVersion()), 700 | 'To get the latest internationalization data upgrade the ICU system package and the intl PHP extension.' 701 | ); 702 | if (\Symfony\Component\Intl\Intl::getIcuDataVersion() <= \Symfony\Component\Intl\Intl::getIcuVersion()) { 703 | $this->addRecommendation( 704 | \Symfony\Component\Intl\Intl::getIcuDataVersion() === \Symfony\Component\Intl\Intl::getIcuVersion(), 705 | sprintf('intl ICU version installed on your system (%s) does not match the ICU data bundled with Symfony (%s)', \Symfony\Component\Intl\Intl::getIcuVersion(), \Symfony\Component\Intl\Intl::getIcuDataVersion()), 706 | 'To avoid internationalization data inconsistencies upgrade the symfony/intl component.' 707 | ); 708 | } 709 | } 710 | 711 | $this->addPhpIniRecommendation( 712 | 'intl.error_level', 713 | create_function('$cfgValue', 'return (int) $cfgValue === 0;'), 714 | true, 715 | 'intl.error_level should be 0 in php.ini', 716 | 'Set "intl.error_level" to "0" in php.ini* to inhibit the messages when an error occurs in ICU functions.' 717 | ); 718 | } 719 | 720 | $accelerator = 721 | (extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) 722 | || 723 | (extension_loaded('apc') && ini_get('apc.enabled')) 724 | || 725 | (extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.enable')) 726 | || 727 | (extension_loaded('Zend OPcache') && ini_get('opcache.enable')) 728 | || 729 | (extension_loaded('xcache') && ini_get('xcache.cacher')) 730 | || 731 | (extension_loaded('wincache') && ini_get('wincache.ocenabled')) 732 | ; 733 | 734 | $this->addRecommendation( 735 | $accelerator, 736 | 'a PHP accelerator should be installed', 737 | 'Install and/or enable a PHP accelerator (highly recommended).' 738 | ); 739 | 740 | if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 741 | $this->addRecommendation( 742 | $this->getRealpathCacheSize() >= 5 * 1024 * 1024, 743 | 'realpath_cache_size should be at least 5M in php.ini', 744 | 'Setting "realpath_cache_size" to e.g. "5242880" or "5M" in php.ini* may improve performance on Windows significantly in some cases.' 745 | ); 746 | } 747 | 748 | $this->addPhpIniRecommendation('short_open_tag', false); 749 | 750 | $this->addPhpIniRecommendation('magic_quotes_gpc', false, true); 751 | 752 | $this->addPhpIniRecommendation('register_globals', false, true); 753 | 754 | $this->addPhpIniRecommendation('session.auto_start', false); 755 | 756 | $this->addRecommendation( 757 | class_exists('PDO'), 758 | 'PDO should be installed', 759 | 'Install PDO (mandatory for Doctrine).' 760 | ); 761 | 762 | if (class_exists('PDO')) { 763 | $drivers = PDO::getAvailableDrivers(); 764 | $this->addRecommendation( 765 | count($drivers) > 0, 766 | sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'), 767 | 'Install PDO drivers (mandatory for Doctrine).' 768 | ); 769 | } 770 | } 771 | 772 | /** 773 | * Loads realpath_cache_size from php.ini and converts it to int. 774 | * 775 | * (e.g. 16k is converted to 16384 int) 776 | * 777 | * @return int 778 | */ 779 | protected function getRealpathCacheSize() 780 | { 781 | $size = ini_get('realpath_cache_size'); 782 | $size = trim($size); 783 | $unit = ''; 784 | if (!ctype_digit($size)) { 785 | $unit = strtolower(substr($size, -1, 1)); 786 | $size = (int) substr($size, 0, -1); 787 | } 788 | switch ($unit) { 789 | case 'g': 790 | return $size * 1024 * 1024 * 1024; 791 | case 'm': 792 | return $size * 1024 * 1024; 793 | case 'k': 794 | return $size * 1024; 795 | default: 796 | return (int) $size; 797 | } 798 | } 799 | 800 | /** 801 | * Defines PHP required version from Symfony version. 802 | * 803 | * @return string|false The PHP required version or false if it could not be guessed 804 | */ 805 | protected function getPhpRequiredVersion() 806 | { 807 | if (!file_exists($path = __DIR__.'/../composer.lock')) { 808 | return false; 809 | } 810 | 811 | $composerLock = json_decode(file_get_contents($path), true); 812 | foreach ($composerLock['packages'] as $package) { 813 | $name = $package['name']; 814 | if ('symfony/symfony' !== $name && 'symfony/http-kernel' !== $name) { 815 | continue; 816 | } 817 | 818 | return (int) $package['version'][1] > 2 ? self::REQUIRED_PHP_VERSION : self::LEGACY_REQUIRED_PHP_VERSION; 819 | } 820 | 821 | return false; 822 | } 823 | } 824 | -------------------------------------------------------------------------------- /app/autoload.php: -------------------------------------------------------------------------------- 1 | getPhpIniConfigPath(); 8 | 9 | echo_title('Symfony Requirements Checker'); 10 | 11 | echo '> PHP is using the following php.ini file:'.PHP_EOL; 12 | if ($iniPath) { 13 | echo_style('green', ' '.$iniPath); 14 | } else { 15 | echo_style('yellow', ' WARNING: No configuration file (php.ini) used by PHP!'); 16 | } 17 | 18 | echo PHP_EOL.PHP_EOL; 19 | 20 | echo '> Checking Symfony requirements:'.PHP_EOL.' '; 21 | 22 | $messages = array(); 23 | foreach ($symfonyRequirements->getRequirements() as $req) { 24 | if ($helpText = get_error_message($req, $lineSize)) { 25 | echo_style('red', 'E'); 26 | $messages['error'][] = $helpText; 27 | } else { 28 | echo_style('green', '.'); 29 | } 30 | } 31 | 32 | $checkPassed = empty($messages['error']); 33 | 34 | foreach ($symfonyRequirements->getRecommendations() as $req) { 35 | if ($helpText = get_error_message($req, $lineSize)) { 36 | echo_style('yellow', 'W'); 37 | $messages['warning'][] = $helpText; 38 | } else { 39 | echo_style('green', '.'); 40 | } 41 | } 42 | 43 | if ($checkPassed) { 44 | echo_block('success', 'OK', 'Your system is ready to run Symfony projects'); 45 | } else { 46 | echo_block('error', 'ERROR', 'Your system is not ready to run Symfony projects'); 47 | 48 | echo_title('Fix the following mandatory requirements', 'red'); 49 | 50 | foreach ($messages['error'] as $helpText) { 51 | echo ' * '.$helpText.PHP_EOL; 52 | } 53 | } 54 | 55 | if (!empty($messages['warning'])) { 56 | echo_title('Optional recommendations to improve your setup', 'yellow'); 57 | 58 | foreach ($messages['warning'] as $helpText) { 59 | echo ' * '.$helpText.PHP_EOL; 60 | } 61 | } 62 | 63 | echo PHP_EOL; 64 | echo_style('title', 'Note'); 65 | echo ' The command console could use a different php.ini file'.PHP_EOL; 66 | echo_style('title', '~~~~'); 67 | echo ' than the one used with your web server. To be on the'.PHP_EOL; 68 | echo ' safe side, please check the requirements from your web'.PHP_EOL; 69 | echo ' server using the '; 70 | echo_style('yellow', 'web/config.php'); 71 | echo ' script.'.PHP_EOL; 72 | echo PHP_EOL; 73 | 74 | exit($checkPassed ? 0 : 1); 75 | 76 | function get_error_message(Requirement $requirement, $lineSize) 77 | { 78 | if ($requirement->isFulfilled()) { 79 | return; 80 | } 81 | 82 | $errorMessage = wordwrap($requirement->getTestMessage(), $lineSize - 3, PHP_EOL.' ').PHP_EOL; 83 | $errorMessage .= ' > '.wordwrap($requirement->getHelpText(), $lineSize - 5, PHP_EOL.' > ').PHP_EOL; 84 | 85 | return $errorMessage; 86 | } 87 | 88 | function echo_title($title, $style = null) 89 | { 90 | $style = $style ?: 'title'; 91 | 92 | echo PHP_EOL; 93 | echo_style($style, $title.PHP_EOL); 94 | echo_style($style, str_repeat('~', strlen($title)).PHP_EOL); 95 | echo PHP_EOL; 96 | } 97 | 98 | function echo_style($style, $message) 99 | { 100 | // ANSI color codes 101 | $styles = array( 102 | 'reset' => "\033[0m", 103 | 'red' => "\033[31m", 104 | 'green' => "\033[32m", 105 | 'yellow' => "\033[33m", 106 | 'error' => "\033[37;41m", 107 | 'success' => "\033[37;42m", 108 | 'title' => "\033[34m", 109 | ); 110 | $supports = has_color_support(); 111 | 112 | echo($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : ''); 113 | } 114 | 115 | function echo_block($style, $title, $message) 116 | { 117 | $message = ' '.trim($message).' '; 118 | $width = strlen($message); 119 | 120 | echo PHP_EOL.PHP_EOL; 121 | 122 | echo_style($style, str_repeat(' ', $width)); 123 | echo PHP_EOL; 124 | echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT)); 125 | echo PHP_EOL; 126 | echo_style($style, $message); 127 | echo PHP_EOL; 128 | echo_style($style, str_repeat(' ', $width)); 129 | echo PHP_EOL; 130 | } 131 | 132 | function has_color_support() 133 | { 134 | static $support; 135 | 136 | if (null === $support) { 137 | if (DIRECTORY_SEPARATOR == '\\') { 138 | $support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); 139 | } else { 140 | $support = function_exists('posix_isatty') && @posix_isatty(STDOUT); 141 | } 142 | } 143 | 144 | return $support; 145 | } 146 | -------------------------------------------------------------------------------- /app/config/config.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: parameters.yml } 3 | - { resource: security.yml } 4 | - { resource: services.yml } 5 | 6 | # Put parameters here that don't need to change on each machine where the app is deployed 7 | # http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration 8 | parameters: 9 | locale: en 10 | 11 | framework: 12 | #esi: ~ 13 | #translator: { fallbacks: ["%locale%"] } 14 | secret: "%secret%" 15 | router: 16 | resource: "%kernel.root_dir%/config/routing.yml" 17 | strict_requirements: ~ 18 | form: ~ 19 | csrf_protection: ~ 20 | validation: { enable_annotations: true } 21 | #serializer: { enable_annotations: true } 22 | templating: 23 | engines: ['twig'] 24 | default_locale: "%locale%" 25 | trusted_hosts: ~ 26 | trusted_proxies: ~ 27 | session: 28 | # handler_id set to null will use default session handler from php.ini 29 | handler_id: ~ 30 | fragments: ~ 31 | http_method_override: true 32 | 33 | # Twig Configuration 34 | twig: 35 | debug: "%kernel.debug%" 36 | strict_variables: "%kernel.debug%" 37 | 38 | # Doctrine Configuration 39 | doctrine: 40 | dbal: 41 | default_connection: default 42 | connections: 43 | 44 | default: 45 | server_version: "5.6" 46 | driver: pdo_mysql 47 | host: localhost 48 | port: ~ 49 | dbname: multidb 50 | user: root 51 | password: root 52 | charset: UTF8 53 | 54 | tenant: 55 | server_version: "5.6" 56 | driver: pdo_mysql 57 | host: %database_host% 58 | port: %database_port% 59 | dbname: ~ 60 | user: ~ 61 | password: ~ 62 | wrapper_class: AppBundle\Connection\Wrapper 63 | 64 | orm: 65 | auto_generate_proxy_classes: "%kernel.debug%" 66 | default_entity_manager: default 67 | 68 | entity_managers: 69 | default: 70 | auto_mapping: false # disabled so we can assign entities to our own connections 71 | connection: default 72 | mappings: 73 | AppBundle: 74 | type: yml 75 | mapping: true 76 | dir: %kernel.root_dir%/../src/AppBundle/Resources/config/doctrine/App 77 | prefix: AppBundle\Entity 78 | is_bundle: false 79 | 80 | tenant: 81 | auto_mapping: false # disabled so we can assign entities to our own connections 82 | connection: tenant 83 | mappings: 84 | Invoices: 85 | type: yml 86 | mapping: true 87 | dir: %kernel.root_dir%/../src/AppBundle/Resources/config/doctrine/Client 88 | prefix: Client 89 | is_bundle: false 90 | -------------------------------------------------------------------------------- /app/config/config_dev.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config.yml } 3 | 4 | framework: 5 | router: 6 | resource: "%kernel.root_dir%/config/routing_dev.yml" 7 | strict_requirements: true 8 | profiler: { only_exceptions: false } 9 | 10 | web_profiler: 11 | toolbar: true 12 | intercept_redirects: false 13 | 14 | monolog: 15 | handlers: 16 | main: 17 | type: stream 18 | path: "%kernel.logs_dir%/%kernel.environment%.log" 19 | level: debug 20 | channels: [!event] 21 | console: 22 | type: console 23 | channels: [!event, !doctrine] 24 | # uncomment to get logging in your browser 25 | # you may have to allow bigger header sizes in your Web server configuration 26 | #firephp: 27 | # type: firephp 28 | # level: info 29 | #chromephp: 30 | # type: chromephp 31 | # level: info 32 | 33 | #swiftmailer: 34 | # delivery_addresses: ['me@example.com'] 35 | -------------------------------------------------------------------------------- /app/config/config_prod.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config.yml } 3 | 4 | #framework: 5 | # validation: 6 | # cache: validator.mapping.cache.doctrine.apc 7 | # serializer: 8 | # cache: serializer.mapping.cache.apc 9 | 10 | #doctrine: 11 | # orm: 12 | # metadata_cache_driver: apc 13 | # result_cache_driver: apc 14 | # query_cache_driver: apc 15 | 16 | monolog: 17 | handlers: 18 | main: 19 | type: fingers_crossed 20 | action_level: error 21 | handler: nested 22 | nested: 23 | type: stream 24 | path: "%kernel.logs_dir%/%kernel.environment%.log" 25 | level: debug 26 | console: 27 | type: console 28 | -------------------------------------------------------------------------------- /app/config/config_test.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config_dev.yml } 3 | 4 | framework: 5 | test: ~ 6 | session: 7 | storage_id: session.storage.mock_file 8 | profiler: 9 | collect: false 10 | 11 | web_profiler: 12 | toolbar: false 13 | intercept_redirects: false 14 | 15 | swiftmailer: 16 | disable_delivery: true 17 | -------------------------------------------------------------------------------- /app/config/parameters.yml.dist: -------------------------------------------------------------------------------- 1 | # This file is a "template" of what your parameters.yml file should look like 2 | # Set parameters here that may be different on each deployment target of the app, e.g. development, staging, production. 3 | # http://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration 4 | parameters: 5 | database_host: 127.0.0.1 6 | database_port: ~ 7 | database_name: symfony 8 | database_user: root 9 | database_password: ~ 10 | # You should uncomment this if you want use pdo_sqlite 11 | # database_path: "%kernel.root_dir%/data.db3" 12 | 13 | mailer_transport: smtp 14 | mailer_host: 127.0.0.1 15 | mailer_user: ~ 16 | mailer_password: ~ 17 | 18 | # A secret key that's used to generate certain security-related tokens 19 | secret: ThisTokenIsNotSoSecretChangeIt 20 | -------------------------------------------------------------------------------- /app/config/routing.yml: -------------------------------------------------------------------------------- 1 | app: 2 | resource: "@AppBundle/Controller/" 3 | type: annotation 4 | -------------------------------------------------------------------------------- /app/config/routing_dev.yml: -------------------------------------------------------------------------------- 1 | _wdt: 2 | resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" 3 | prefix: /_wdt 4 | 5 | _profiler: 6 | resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" 7 | prefix: /_profiler 8 | 9 | _errors: 10 | resource: "@TwigBundle/Resources/config/routing/errors.xml" 11 | prefix: /_error 12 | 13 | _main: 14 | resource: routing.yml 15 | -------------------------------------------------------------------------------- /app/config/security.yml: -------------------------------------------------------------------------------- 1 | # To get started with security, check out the documentation: 2 | # http://symfony.com/doc/current/security.html 3 | security: 4 | 5 | # http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded 6 | providers: 7 | in_memory: 8 | memory: ~ 9 | 10 | firewalls: 11 | # disables authentication for assets and the profiler, adapt it according to your needs 12 | dev: 13 | pattern: ^/(_(profiler|wdt)|css|images|js)/ 14 | security: false 15 | 16 | main: 17 | anonymous: ~ 18 | # activate different ways to authenticate 19 | 20 | # http_basic: ~ 21 | # http://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate 22 | 23 | # form_login: ~ 24 | # http://symfony.com/doc/current/cookbook/security/form_login_setup.html 25 | -------------------------------------------------------------------------------- /app/config/services.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | allowed_commands: 3 | - doctrine:database:create 4 | - doctrine:database:drop 5 | - doctrine:schema:update 6 | - doctrine:schema:create 7 | - doctrine:schema:drop 8 | - doctrine:fixtures:load 9 | - doctrine:migrations:diff 10 | - doctrine:migrations:execute 11 | - doctrine:migrations:generate 12 | - doctrine:migrations:migrate 13 | - doctrine:migrations:version 14 | - doctrine:migrations:status 15 | - doctrine:mapping:info 16 | 17 | fixtures: 18 | default: %kernel.root_dir%/../src/AppBundle/DataFixtures/Standard 19 | tenant: %kernel.root_dir%/../src/AppBundle/DataFixtures/Tenant 20 | 21 | services: 22 | app_bundle.collector.db_connections: 23 | class: AppBundle\Collector\MultiDbCollector 24 | arguments: 25 | - @doctrine.dbal.default_connection 26 | - @doctrine.dbal.tenant_connection 27 | tags: 28 | - { name: data_collector, template: "AppBundle:Toolbar:DatabaseConnections.html.twig", id: "database_connections" } 29 | 30 | app_bundle.command_listener: 31 | class: AppBundle\Listener\CommandListener 32 | arguments: 33 | - @app_bundle.repository.tenant 34 | - @doctrine.dbal.tenant_connection 35 | - @=service('doctrine.dbal.default_connection').getSchemaManager() 36 | - %allowed_commands% 37 | tags: 38 | - { name: kernel.event_listener, event: console.command, method: onConsoleCommand, priority: 255 } 39 | 40 | app_bundle.fixtures_listener: 41 | class: AppBundle\Listener\FixturesListener 42 | arguments: 43 | - %fixtures% 44 | tags: 45 | - { name: kernel.event_listener, event: console.command, method: onConsoleCommand, priority: 254 } 46 | 47 | app_bundle.dbma_listener: 48 | class: AppBundle\Listener\DatabaseListener 49 | tags: 50 | - { name: kernel.event_listener, event: console.command, method: onConsoleCommand, priority: 254 } 51 | 52 | 53 | app_bundle.request_listener: 54 | class: AppBundle\Listener\TenantListener 55 | arguments: 56 | - @doctrine.dbal.tenant_connection 57 | - @app_bundle.repository.tenant 58 | tags: 59 | - {name: kernel.event_listener, event: kernel.request, method: onKernelRequest } 60 | 61 | app_bundle.repository.tenant: 62 | class: AppBundle\Entity\TenantRepository 63 | factory: 'doctrine.orm.default_entity_manager:getRepository' 64 | arguments: 65 | - AppBundle\Entity\Tenant 66 | 67 | app_bundle.repository.invoice: 68 | class: Client\InvoiceRepository 69 | factory: 'doctrine.orm.tenant_entity_manager:getRepository' 70 | arguments: 71 | - Client\Invoice 72 | -------------------------------------------------------------------------------- /app/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev'); 20 | $debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod'; 21 | 22 | if ($debug) { 23 | Debug::enable(); 24 | } 25 | 26 | $kernel = new AppKernel($env, $debug); 27 | $application = new Application($kernel); 28 | $application->run($input); 29 | -------------------------------------------------------------------------------- /app/logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uirapuru/multidb/0953d48083cb3374633a982d8ba6ae6a928c0c3f/app/logs/.gitkeep -------------------------------------------------------------------------------- /app/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | ../src/*/*Bundle/Tests 20 | ../src/*/Bundle/*Bundle/Tests 21 | ../src/*Bundle/Tests 22 | 23 | 24 | 25 | 26 | 27 | ../src 28 | 29 | ../src/*Bundle/Resources 30 | ../src/*Bundle/Tests 31 | ../src/*/*Bundle/Resources 32 | ../src/*/*Bundle/Tests 33 | ../src/*/Bundle/*Bundle/Resources 34 | ../src/*/Bundle/*Bundle/Tests 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uirapuru/multidb", 3 | "license": "proprietary", 4 | "type": "project", 5 | "autoload": { 6 | "psr-4": { 7 | "": "src/" 8 | }, 9 | "classmap": [ 10 | "app/AppKernel.php", 11 | "app/AppCache.php" 12 | ] 13 | }, 14 | "require": { 15 | "php": ">=5.3.9", 16 | "symfony/symfony": "2.8.*", 17 | "doctrine/orm": "^2.4.8", 18 | "doctrine/doctrine-bundle": "~1.4", 19 | "symfony/swiftmailer-bundle": "~2.3,>=2.3.10", 20 | "symfony/monolog-bundle": "^3.0.2", 21 | "sensio/distribution-bundle": "~5.0", 22 | "sensio/framework-extra-bundle": "^3.0.2", 23 | "incenteev/composer-parameter-handler": "~2.0" 24 | }, 25 | "require-dev": { 26 | "sensio/generator-bundle": "~3.0", 27 | "symfony/phpunit-bridge": "~2.7", 28 | "doctrine/doctrine-fixtures-bundle": "^2.3" 29 | }, 30 | "scripts": { 31 | "symfony-scripts": [ 32 | "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", 33 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", 34 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", 35 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", 36 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", 37 | "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget" 38 | ], 39 | "post-install-cmd": [ 40 | "@symfony-scripts" 41 | ], 42 | "post-update-cmd": [ 43 | "@symfony-scripts" 44 | ], 45 | "prepare-default": [ 46 | "php app/console doctrine:database:drop --if-exists --no-interaction --force --quiet", 47 | "php app/console doctrine:database:create --if-not-exists --no-interaction --quiet", 48 | "php app/console doctrine:schema:create --no-interaction --quiet", 49 | "php app/console doctrine:fixtures:load --no-interaction --quiet" 50 | ], 51 | "prepare-tenants": [ 52 | "php app/console doctrine:database:drop --tenant=batman --if-exists --no-interaction --force --quiet", 53 | "php app/console doctrine:database:create --tenant=batman --if-not-exists --no-interaction --quiet", 54 | "php app/console doctrine:schema:create --tenant=batman --no-interaction --quiet", 55 | "php app/console doctrine:fixtures:load --tenant=batman --no-interaction --quiet", 56 | 57 | "php app/console doctrine:database:drop --tenant=spiderman --if-exists --no-interaction --force --quiet", 58 | "php app/console doctrine:database:create --tenant=spiderman --if-not-exists --no-interaction --quiet", 59 | "php app/console doctrine:schema:create --tenant=spiderman --no-interaction --quiet", 60 | 61 | "php app/console doctrine:database:drop --tenant=superman --if-exists --no-interaction --force --quiet", 62 | "php app/console doctrine:database:create --tenant=superman --if-not-exists --no-interaction --quiet", 63 | "php app/console doctrine:schema:create --tenant=superman --no-interaction --quiet", 64 | "php app/console doctrine:fixtures:load --tenant=superman --no-interaction --quiet" 65 | ] 66 | }, 67 | "config": { 68 | "bin-dir": "bin" 69 | }, 70 | "extra": { 71 | "symfony-app-dir": "app", 72 | "symfony-web-dir": "web", 73 | "symfony-assets-install": "relative", 74 | "incenteev-parameters": { 75 | "file": "app/config/parameters.yml" 76 | }, 77 | "branch-alias": null 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "940efb175345071a2be3f599c6bf91a0", 8 | "content-hash": "0541cd65a17e756e5a7f85c5f5b6c6df", 9 | "packages": [ 10 | { 11 | "name": "doctrine/annotations", 12 | "version": "v1.2.7", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/doctrine/annotations.git", 16 | "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", 21 | "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "doctrine/lexer": "1.*", 26 | "php": ">=5.3.2" 27 | }, 28 | "require-dev": { 29 | "doctrine/cache": "1.*", 30 | "phpunit/phpunit": "4.*" 31 | }, 32 | "type": "library", 33 | "extra": { 34 | "branch-alias": { 35 | "dev-master": "1.3.x-dev" 36 | } 37 | }, 38 | "autoload": { 39 | "psr-0": { 40 | "Doctrine\\Common\\Annotations\\": "lib/" 41 | } 42 | }, 43 | "notification-url": "https://packagist.org/downloads/", 44 | "license": [ 45 | "MIT" 46 | ], 47 | "authors": [ 48 | { 49 | "name": "Roman Borschel", 50 | "email": "roman@code-factory.org" 51 | }, 52 | { 53 | "name": "Benjamin Eberlei", 54 | "email": "kontakt@beberlei.de" 55 | }, 56 | { 57 | "name": "Guilherme Blanco", 58 | "email": "guilhermeblanco@gmail.com" 59 | }, 60 | { 61 | "name": "Jonathan Wage", 62 | "email": "jonwage@gmail.com" 63 | }, 64 | { 65 | "name": "Johannes Schmitt", 66 | "email": "schmittjoh@gmail.com" 67 | } 68 | ], 69 | "description": "Docblock Annotations Parser", 70 | "homepage": "http://www.doctrine-project.org", 71 | "keywords": [ 72 | "annotations", 73 | "docblock", 74 | "parser" 75 | ], 76 | "time": "2015-08-31 12:32:49" 77 | }, 78 | { 79 | "name": "doctrine/cache", 80 | "version": "v1.5.4", 81 | "source": { 82 | "type": "git", 83 | "url": "https://github.com/doctrine/cache.git", 84 | "reference": "47cdc76ceb95cc591d9c79a36dc3794975b5d136" 85 | }, 86 | "dist": { 87 | "type": "zip", 88 | "url": "https://api.github.com/repos/doctrine/cache/zipball/47cdc76ceb95cc591d9c79a36dc3794975b5d136", 89 | "reference": "47cdc76ceb95cc591d9c79a36dc3794975b5d136", 90 | "shasum": "" 91 | }, 92 | "require": { 93 | "php": ">=5.3.2" 94 | }, 95 | "conflict": { 96 | "doctrine/common": ">2.2,<2.4" 97 | }, 98 | "require-dev": { 99 | "phpunit/phpunit": ">=3.7", 100 | "predis/predis": "~1.0", 101 | "satooshi/php-coveralls": "~0.6" 102 | }, 103 | "type": "library", 104 | "extra": { 105 | "branch-alias": { 106 | "dev-master": "1.5.x-dev" 107 | } 108 | }, 109 | "autoload": { 110 | "psr-4": { 111 | "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" 112 | } 113 | }, 114 | "notification-url": "https://packagist.org/downloads/", 115 | "license": [ 116 | "MIT" 117 | ], 118 | "authors": [ 119 | { 120 | "name": "Roman Borschel", 121 | "email": "roman@code-factory.org" 122 | }, 123 | { 124 | "name": "Benjamin Eberlei", 125 | "email": "kontakt@beberlei.de" 126 | }, 127 | { 128 | "name": "Guilherme Blanco", 129 | "email": "guilhermeblanco@gmail.com" 130 | }, 131 | { 132 | "name": "Jonathan Wage", 133 | "email": "jonwage@gmail.com" 134 | }, 135 | { 136 | "name": "Johannes Schmitt", 137 | "email": "schmittjoh@gmail.com" 138 | } 139 | ], 140 | "description": "Caching library offering an object-oriented API for many cache backends", 141 | "homepage": "http://www.doctrine-project.org", 142 | "keywords": [ 143 | "cache", 144 | "caching" 145 | ], 146 | "time": "2015-12-19 05:03:47" 147 | }, 148 | { 149 | "name": "doctrine/collections", 150 | "version": "v1.3.0", 151 | "source": { 152 | "type": "git", 153 | "url": "https://github.com/doctrine/collections.git", 154 | "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a" 155 | }, 156 | "dist": { 157 | "type": "zip", 158 | "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a", 159 | "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a", 160 | "shasum": "" 161 | }, 162 | "require": { 163 | "php": ">=5.3.2" 164 | }, 165 | "require-dev": { 166 | "phpunit/phpunit": "~4.0" 167 | }, 168 | "type": "library", 169 | "extra": { 170 | "branch-alias": { 171 | "dev-master": "1.2.x-dev" 172 | } 173 | }, 174 | "autoload": { 175 | "psr-0": { 176 | "Doctrine\\Common\\Collections\\": "lib/" 177 | } 178 | }, 179 | "notification-url": "https://packagist.org/downloads/", 180 | "license": [ 181 | "MIT" 182 | ], 183 | "authors": [ 184 | { 185 | "name": "Roman Borschel", 186 | "email": "roman@code-factory.org" 187 | }, 188 | { 189 | "name": "Benjamin Eberlei", 190 | "email": "kontakt@beberlei.de" 191 | }, 192 | { 193 | "name": "Guilherme Blanco", 194 | "email": "guilhermeblanco@gmail.com" 195 | }, 196 | { 197 | "name": "Jonathan Wage", 198 | "email": "jonwage@gmail.com" 199 | }, 200 | { 201 | "name": "Johannes Schmitt", 202 | "email": "schmittjoh@gmail.com" 203 | } 204 | ], 205 | "description": "Collections Abstraction library", 206 | "homepage": "http://www.doctrine-project.org", 207 | "keywords": [ 208 | "array", 209 | "collections", 210 | "iterator" 211 | ], 212 | "time": "2015-04-14 22:21:58" 213 | }, 214 | { 215 | "name": "doctrine/common", 216 | "version": "v2.5.3", 217 | "source": { 218 | "type": "git", 219 | "url": "https://github.com/doctrine/common.git", 220 | "reference": "10f1f19651343f87573129ca970aef1a47a6f29e" 221 | }, 222 | "dist": { 223 | "type": "zip", 224 | "url": "https://api.github.com/repos/doctrine/common/zipball/10f1f19651343f87573129ca970aef1a47a6f29e", 225 | "reference": "10f1f19651343f87573129ca970aef1a47a6f29e", 226 | "shasum": "" 227 | }, 228 | "require": { 229 | "doctrine/annotations": "1.*", 230 | "doctrine/cache": "1.*", 231 | "doctrine/collections": "1.*", 232 | "doctrine/inflector": "1.*", 233 | "doctrine/lexer": "1.*", 234 | "php": ">=5.3.2" 235 | }, 236 | "require-dev": { 237 | "phpunit/phpunit": "~3.7" 238 | }, 239 | "type": "library", 240 | "extra": { 241 | "branch-alias": { 242 | "dev-master": "2.5.x-dev" 243 | } 244 | }, 245 | "autoload": { 246 | "psr-0": { 247 | "Doctrine\\Common\\": "lib/" 248 | } 249 | }, 250 | "notification-url": "https://packagist.org/downloads/", 251 | "license": [ 252 | "MIT" 253 | ], 254 | "authors": [ 255 | { 256 | "name": "Roman Borschel", 257 | "email": "roman@code-factory.org" 258 | }, 259 | { 260 | "name": "Benjamin Eberlei", 261 | "email": "kontakt@beberlei.de" 262 | }, 263 | { 264 | "name": "Guilherme Blanco", 265 | "email": "guilhermeblanco@gmail.com" 266 | }, 267 | { 268 | "name": "Jonathan Wage", 269 | "email": "jonwage@gmail.com" 270 | }, 271 | { 272 | "name": "Johannes Schmitt", 273 | "email": "schmittjoh@gmail.com" 274 | } 275 | ], 276 | "description": "Common Library for Doctrine projects", 277 | "homepage": "http://www.doctrine-project.org", 278 | "keywords": [ 279 | "annotations", 280 | "collections", 281 | "eventmanager", 282 | "persistence", 283 | "spl" 284 | ], 285 | "time": "2015-12-25 13:10:16" 286 | }, 287 | { 288 | "name": "doctrine/dbal", 289 | "version": "v2.5.6", 290 | "source": { 291 | "type": "git", 292 | "url": "https://github.com/doctrine/dbal.git", 293 | "reference": "a526d0df58daa8ab6802244bf3227f532e0deb45" 294 | }, 295 | "dist": { 296 | "type": "zip", 297 | "url": "https://api.github.com/repos/doctrine/dbal/zipball/a526d0df58daa8ab6802244bf3227f532e0deb45", 298 | "reference": "a526d0df58daa8ab6802244bf3227f532e0deb45", 299 | "shasum": "" 300 | }, 301 | "require": { 302 | "doctrine/common": ">=2.4,<2.8-dev", 303 | "php": ">=5.3.2" 304 | }, 305 | "require-dev": { 306 | "phpunit/phpunit": "4.*", 307 | "symfony/console": "2.*||^3.0" 308 | }, 309 | "suggest": { 310 | "symfony/console": "For helpful console commands such as SQL execution and import of files." 311 | }, 312 | "bin": [ 313 | "bin/doctrine-dbal" 314 | ], 315 | "type": "library", 316 | "extra": { 317 | "branch-alias": { 318 | "dev-master": "2.5.x-dev" 319 | } 320 | }, 321 | "autoload": { 322 | "psr-0": { 323 | "Doctrine\\DBAL\\": "lib/" 324 | } 325 | }, 326 | "notification-url": "https://packagist.org/downloads/", 327 | "license": [ 328 | "MIT" 329 | ], 330 | "authors": [ 331 | { 332 | "name": "Roman Borschel", 333 | "email": "roman@code-factory.org" 334 | }, 335 | { 336 | "name": "Benjamin Eberlei", 337 | "email": "kontakt@beberlei.de" 338 | }, 339 | { 340 | "name": "Guilherme Blanco", 341 | "email": "guilhermeblanco@gmail.com" 342 | }, 343 | { 344 | "name": "Jonathan Wage", 345 | "email": "jonwage@gmail.com" 346 | } 347 | ], 348 | "description": "Database Abstraction Layer", 349 | "homepage": "http://www.doctrine-project.org", 350 | "keywords": [ 351 | "database", 352 | "dbal", 353 | "persistence", 354 | "queryobject" 355 | ], 356 | "time": "2016-12-04 05:53:43" 357 | }, 358 | { 359 | "name": "doctrine/doctrine-bundle", 360 | "version": "1.6.4", 361 | "source": { 362 | "type": "git", 363 | "url": "https://github.com/doctrine/DoctrineBundle.git", 364 | "reference": "dd40b0a7fb16658cda9def9786992b8df8a49be7" 365 | }, 366 | "dist": { 367 | "type": "zip", 368 | "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/dd40b0a7fb16658cda9def9786992b8df8a49be7", 369 | "reference": "dd40b0a7fb16658cda9def9786992b8df8a49be7", 370 | "shasum": "" 371 | }, 372 | "require": { 373 | "doctrine/dbal": "~2.3", 374 | "doctrine/doctrine-cache-bundle": "~1.0", 375 | "jdorn/sql-formatter": "~1.1", 376 | "php": ">=5.3.2", 377 | "symfony/console": "~2.3|~3.0", 378 | "symfony/dependency-injection": "~2.3|~3.0", 379 | "symfony/doctrine-bridge": "~2.2|~3.0", 380 | "symfony/framework-bundle": "~2.3|~3.0" 381 | }, 382 | "require-dev": { 383 | "doctrine/orm": "~2.3", 384 | "phpunit/phpunit": "~4", 385 | "satooshi/php-coveralls": "~0.6.1", 386 | "symfony/phpunit-bridge": "~2.7|~3.0", 387 | "symfony/property-info": "~2.8|~3.0", 388 | "symfony/validator": "~2.2|~3.0", 389 | "symfony/yaml": "~2.2|~3.0", 390 | "twig/twig": "~1.10" 391 | }, 392 | "suggest": { 393 | "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", 394 | "symfony/web-profiler-bundle": "To use the data collector." 395 | }, 396 | "type": "symfony-bundle", 397 | "extra": { 398 | "branch-alias": { 399 | "dev-master": "1.6.x-dev" 400 | } 401 | }, 402 | "autoload": { 403 | "psr-4": { 404 | "Doctrine\\Bundle\\DoctrineBundle\\": "" 405 | } 406 | }, 407 | "notification-url": "https://packagist.org/downloads/", 408 | "license": [ 409 | "MIT" 410 | ], 411 | "authors": [ 412 | { 413 | "name": "Symfony Community", 414 | "homepage": "http://symfony.com/contributors" 415 | }, 416 | { 417 | "name": "Benjamin Eberlei", 418 | "email": "kontakt@beberlei.de" 419 | }, 420 | { 421 | "name": "Doctrine Project", 422 | "homepage": "http://www.doctrine-project.org/" 423 | }, 424 | { 425 | "name": "Fabien Potencier", 426 | "email": "fabien@symfony.com" 427 | } 428 | ], 429 | "description": "Symfony DoctrineBundle", 430 | "homepage": "http://www.doctrine-project.org", 431 | "keywords": [ 432 | "database", 433 | "dbal", 434 | "orm", 435 | "persistence" 436 | ], 437 | "time": "2016-08-10 15:35:22" 438 | }, 439 | { 440 | "name": "doctrine/doctrine-cache-bundle", 441 | "version": "1.3.0", 442 | "source": { 443 | "type": "git", 444 | "url": "https://github.com/doctrine/DoctrineCacheBundle.git", 445 | "reference": "18c600a9b82f6454d2e81ca4957cdd56a1cf3504" 446 | }, 447 | "dist": { 448 | "type": "zip", 449 | "url": "https://api.github.com/repos/doctrine/DoctrineCacheBundle/zipball/18c600a9b82f6454d2e81ca4957cdd56a1cf3504", 450 | "reference": "18c600a9b82f6454d2e81ca4957cdd56a1cf3504", 451 | "shasum": "" 452 | }, 453 | "require": { 454 | "doctrine/cache": "^1.4.2", 455 | "doctrine/inflector": "~1.0", 456 | "php": ">=5.3.2", 457 | "symfony/doctrine-bridge": "~2.2|~3.0" 458 | }, 459 | "require-dev": { 460 | "instaclick/coding-standard": "~1.1", 461 | "instaclick/object-calisthenics-sniffs": "dev-master", 462 | "instaclick/symfony2-coding-standard": "dev-remaster", 463 | "phpunit/phpunit": "~4", 464 | "predis/predis": "~0.8", 465 | "satooshi/php-coveralls": "~0.6.1", 466 | "squizlabs/php_codesniffer": "~1.5", 467 | "symfony/console": "~2.2|~3.0", 468 | "symfony/finder": "~2.2|~3.0", 469 | "symfony/framework-bundle": "~2.2|~3.0", 470 | "symfony/phpunit-bridge": "~2.7|~3.0", 471 | "symfony/security-acl": "~2.3|~3.0", 472 | "symfony/validator": "~2.2|~3.0", 473 | "symfony/yaml": "~2.2|~3.0" 474 | }, 475 | "suggest": { 476 | "symfony/security-acl": "For using this bundle to cache ACLs" 477 | }, 478 | "type": "symfony-bundle", 479 | "extra": { 480 | "branch-alias": { 481 | "dev-master": "1.2.x-dev" 482 | } 483 | }, 484 | "autoload": { 485 | "psr-4": { 486 | "Doctrine\\Bundle\\DoctrineCacheBundle\\": "" 487 | } 488 | }, 489 | "notification-url": "https://packagist.org/downloads/", 490 | "license": [ 491 | "MIT" 492 | ], 493 | "authors": [ 494 | { 495 | "name": "Symfony Community", 496 | "homepage": "http://symfony.com/contributors" 497 | }, 498 | { 499 | "name": "Benjamin Eberlei", 500 | "email": "kontakt@beberlei.de" 501 | }, 502 | { 503 | "name": "Fabio B. Silva", 504 | "email": "fabio.bat.silva@gmail.com" 505 | }, 506 | { 507 | "name": "Guilherme Blanco", 508 | "email": "guilhermeblanco@hotmail.com" 509 | }, 510 | { 511 | "name": "Doctrine Project", 512 | "homepage": "http://www.doctrine-project.org/" 513 | }, 514 | { 515 | "name": "Fabien Potencier", 516 | "email": "fabien@symfony.com" 517 | } 518 | ], 519 | "description": "Symfony Bundle for Doctrine Cache", 520 | "homepage": "http://www.doctrine-project.org", 521 | "keywords": [ 522 | "cache", 523 | "caching" 524 | ], 525 | "time": "2016-01-26 17:28:51" 526 | }, 527 | { 528 | "name": "doctrine/inflector", 529 | "version": "v1.1.0", 530 | "source": { 531 | "type": "git", 532 | "url": "https://github.com/doctrine/inflector.git", 533 | "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" 534 | }, 535 | "dist": { 536 | "type": "zip", 537 | "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", 538 | "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", 539 | "shasum": "" 540 | }, 541 | "require": { 542 | "php": ">=5.3.2" 543 | }, 544 | "require-dev": { 545 | "phpunit/phpunit": "4.*" 546 | }, 547 | "type": "library", 548 | "extra": { 549 | "branch-alias": { 550 | "dev-master": "1.1.x-dev" 551 | } 552 | }, 553 | "autoload": { 554 | "psr-0": { 555 | "Doctrine\\Common\\Inflector\\": "lib/" 556 | } 557 | }, 558 | "notification-url": "https://packagist.org/downloads/", 559 | "license": [ 560 | "MIT" 561 | ], 562 | "authors": [ 563 | { 564 | "name": "Roman Borschel", 565 | "email": "roman@code-factory.org" 566 | }, 567 | { 568 | "name": "Benjamin Eberlei", 569 | "email": "kontakt@beberlei.de" 570 | }, 571 | { 572 | "name": "Guilherme Blanco", 573 | "email": "guilhermeblanco@gmail.com" 574 | }, 575 | { 576 | "name": "Jonathan Wage", 577 | "email": "jonwage@gmail.com" 578 | }, 579 | { 580 | "name": "Johannes Schmitt", 581 | "email": "schmittjoh@gmail.com" 582 | } 583 | ], 584 | "description": "Common String Manipulations with regard to casing and singular/plural rules.", 585 | "homepage": "http://www.doctrine-project.org", 586 | "keywords": [ 587 | "inflection", 588 | "pluralize", 589 | "singularize", 590 | "string" 591 | ], 592 | "time": "2015-11-06 14:35:42" 593 | }, 594 | { 595 | "name": "doctrine/lexer", 596 | "version": "v1.0.1", 597 | "source": { 598 | "type": "git", 599 | "url": "https://github.com/doctrine/lexer.git", 600 | "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" 601 | }, 602 | "dist": { 603 | "type": "zip", 604 | "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", 605 | "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", 606 | "shasum": "" 607 | }, 608 | "require": { 609 | "php": ">=5.3.2" 610 | }, 611 | "type": "library", 612 | "extra": { 613 | "branch-alias": { 614 | "dev-master": "1.0.x-dev" 615 | } 616 | }, 617 | "autoload": { 618 | "psr-0": { 619 | "Doctrine\\Common\\Lexer\\": "lib/" 620 | } 621 | }, 622 | "notification-url": "https://packagist.org/downloads/", 623 | "license": [ 624 | "MIT" 625 | ], 626 | "authors": [ 627 | { 628 | "name": "Roman Borschel", 629 | "email": "roman@code-factory.org" 630 | }, 631 | { 632 | "name": "Guilherme Blanco", 633 | "email": "guilhermeblanco@gmail.com" 634 | }, 635 | { 636 | "name": "Johannes Schmitt", 637 | "email": "schmittjoh@gmail.com" 638 | } 639 | ], 640 | "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", 641 | "homepage": "http://www.doctrine-project.org", 642 | "keywords": [ 643 | "lexer", 644 | "parser" 645 | ], 646 | "time": "2014-09-09 13:34:57" 647 | }, 648 | { 649 | "name": "doctrine/orm", 650 | "version": "v2.4.8", 651 | "source": { 652 | "type": "git", 653 | "url": "https://github.com/doctrine/doctrine2.git", 654 | "reference": "5aedac1e5c5caaeac14798822c70325dc242d467" 655 | }, 656 | "dist": { 657 | "type": "zip", 658 | "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/5aedac1e5c5caaeac14798822c70325dc242d467", 659 | "reference": "5aedac1e5c5caaeac14798822c70325dc242d467", 660 | "shasum": "" 661 | }, 662 | "require": { 663 | "doctrine/collections": "~1.1", 664 | "doctrine/dbal": "~2.4", 665 | "ext-pdo": "*", 666 | "php": ">=5.3.2", 667 | "symfony/console": "~2.0" 668 | }, 669 | "require-dev": { 670 | "satooshi/php-coveralls": "dev-master", 671 | "symfony/yaml": "~2.1" 672 | }, 673 | "suggest": { 674 | "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" 675 | }, 676 | "bin": [ 677 | "bin/doctrine", 678 | "bin/doctrine.php" 679 | ], 680 | "type": "library", 681 | "extra": { 682 | "branch-alias": { 683 | "dev-master": "2.4.x-dev" 684 | } 685 | }, 686 | "autoload": { 687 | "psr-0": { 688 | "Doctrine\\ORM\\": "lib/" 689 | } 690 | }, 691 | "notification-url": "https://packagist.org/downloads/", 692 | "license": [ 693 | "MIT" 694 | ], 695 | "authors": [ 696 | { 697 | "name": "Roman Borschel", 698 | "email": "roman@code-factory.org" 699 | }, 700 | { 701 | "name": "Benjamin Eberlei", 702 | "email": "kontakt@beberlei.de" 703 | }, 704 | { 705 | "name": "Guilherme Blanco", 706 | "email": "guilhermeblanco@gmail.com" 707 | }, 708 | { 709 | "name": "Jonathan Wage", 710 | "email": "jonwage@gmail.com" 711 | } 712 | ], 713 | "description": "Object-Relational-Mapper for PHP", 714 | "homepage": "http://www.doctrine-project.org", 715 | "keywords": [ 716 | "database", 717 | "orm" 718 | ], 719 | "time": "2015-08-31 13:19:01" 720 | }, 721 | { 722 | "name": "incenteev/composer-parameter-handler", 723 | "version": "v2.1.2", 724 | "source": { 725 | "type": "git", 726 | "url": "https://github.com/Incenteev/ParameterHandler.git", 727 | "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc" 728 | }, 729 | "dist": { 730 | "type": "zip", 731 | "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", 732 | "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", 733 | "shasum": "" 734 | }, 735 | "require": { 736 | "php": ">=5.3.3", 737 | "symfony/yaml": "~2.3|~3.0" 738 | }, 739 | "require-dev": { 740 | "composer/composer": "1.0.*@dev", 741 | "phpspec/prophecy-phpunit": "~1.0", 742 | "symfony/filesystem": "~2.2" 743 | }, 744 | "type": "library", 745 | "extra": { 746 | "branch-alias": { 747 | "dev-master": "2.1.x-dev" 748 | } 749 | }, 750 | "autoload": { 751 | "psr-4": { 752 | "Incenteev\\ParameterHandler\\": "" 753 | } 754 | }, 755 | "notification-url": "https://packagist.org/downloads/", 756 | "license": [ 757 | "MIT" 758 | ], 759 | "authors": [ 760 | { 761 | "name": "Christophe Coevoet", 762 | "email": "stof@notk.org" 763 | } 764 | ], 765 | "description": "Composer script handling your ignored parameter file", 766 | "homepage": "https://github.com/Incenteev/ParameterHandler", 767 | "keywords": [ 768 | "parameters management" 769 | ], 770 | "time": "2015-11-10 17:04:01" 771 | }, 772 | { 773 | "name": "ircmaxell/password-compat", 774 | "version": "v1.0.4", 775 | "source": { 776 | "type": "git", 777 | "url": "https://github.com/ircmaxell/password_compat.git", 778 | "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" 779 | }, 780 | "dist": { 781 | "type": "zip", 782 | "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", 783 | "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", 784 | "shasum": "" 785 | }, 786 | "require-dev": { 787 | "phpunit/phpunit": "4.*" 788 | }, 789 | "type": "library", 790 | "autoload": { 791 | "files": [ 792 | "lib/password.php" 793 | ] 794 | }, 795 | "notification-url": "https://packagist.org/downloads/", 796 | "license": [ 797 | "MIT" 798 | ], 799 | "authors": [ 800 | { 801 | "name": "Anthony Ferrara", 802 | "email": "ircmaxell@php.net", 803 | "homepage": "http://blog.ircmaxell.com" 804 | } 805 | ], 806 | "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", 807 | "homepage": "https://github.com/ircmaxell/password_compat", 808 | "keywords": [ 809 | "hashing", 810 | "password" 811 | ], 812 | "time": "2014-11-20 16:49:30" 813 | }, 814 | { 815 | "name": "jdorn/sql-formatter", 816 | "version": "v1.2.17", 817 | "source": { 818 | "type": "git", 819 | "url": "https://github.com/jdorn/sql-formatter.git", 820 | "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc" 821 | }, 822 | "dist": { 823 | "type": "zip", 824 | "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", 825 | "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", 826 | "shasum": "" 827 | }, 828 | "require": { 829 | "php": ">=5.2.4" 830 | }, 831 | "require-dev": { 832 | "phpunit/phpunit": "3.7.*" 833 | }, 834 | "type": "library", 835 | "extra": { 836 | "branch-alias": { 837 | "dev-master": "1.3.x-dev" 838 | } 839 | }, 840 | "autoload": { 841 | "classmap": [ 842 | "lib" 843 | ] 844 | }, 845 | "notification-url": "https://packagist.org/downloads/", 846 | "license": [ 847 | "MIT" 848 | ], 849 | "authors": [ 850 | { 851 | "name": "Jeremy Dorn", 852 | "email": "jeremy@jeremydorn.com", 853 | "homepage": "http://jeremydorn.com/" 854 | } 855 | ], 856 | "description": "a PHP SQL highlighting library", 857 | "homepage": "https://github.com/jdorn/sql-formatter/", 858 | "keywords": [ 859 | "highlight", 860 | "sql" 861 | ], 862 | "time": "2014-01-12 16:20:24" 863 | }, 864 | { 865 | "name": "monolog/monolog", 866 | "version": "1.22.0", 867 | "source": { 868 | "type": "git", 869 | "url": "https://github.com/Seldaek/monolog.git", 870 | "reference": "bad29cb8d18ab0315e6c477751418a82c850d558" 871 | }, 872 | "dist": { 873 | "type": "zip", 874 | "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bad29cb8d18ab0315e6c477751418a82c850d558", 875 | "reference": "bad29cb8d18ab0315e6c477751418a82c850d558", 876 | "shasum": "" 877 | }, 878 | "require": { 879 | "php": ">=5.3.0", 880 | "psr/log": "~1.0" 881 | }, 882 | "provide": { 883 | "psr/log-implementation": "1.0.0" 884 | }, 885 | "require-dev": { 886 | "aws/aws-sdk-php": "^2.4.9 || ^3.0", 887 | "doctrine/couchdb": "~1.0@dev", 888 | "graylog2/gelf-php": "~1.0", 889 | "jakub-onderka/php-parallel-lint": "0.9", 890 | "php-amqplib/php-amqplib": "~2.4", 891 | "php-console/php-console": "^3.1.3", 892 | "phpunit/phpunit": "~4.5", 893 | "phpunit/phpunit-mock-objects": "2.3.0", 894 | "ruflin/elastica": ">=0.90 <3.0", 895 | "sentry/sentry": "^0.13", 896 | "swiftmailer/swiftmailer": "~5.3" 897 | }, 898 | "suggest": { 899 | "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", 900 | "doctrine/couchdb": "Allow sending log messages to a CouchDB server", 901 | "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", 902 | "ext-mongo": "Allow sending log messages to a MongoDB server", 903 | "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", 904 | "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", 905 | "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", 906 | "php-console/php-console": "Allow sending log messages to Google Chrome", 907 | "rollbar/rollbar": "Allow sending log messages to Rollbar", 908 | "ruflin/elastica": "Allow sending log messages to an Elastic Search server", 909 | "sentry/sentry": "Allow sending log messages to a Sentry server" 910 | }, 911 | "type": "library", 912 | "extra": { 913 | "branch-alias": { 914 | "dev-master": "2.0.x-dev" 915 | } 916 | }, 917 | "autoload": { 918 | "psr-4": { 919 | "Monolog\\": "src/Monolog" 920 | } 921 | }, 922 | "notification-url": "https://packagist.org/downloads/", 923 | "license": [ 924 | "MIT" 925 | ], 926 | "authors": [ 927 | { 928 | "name": "Jordi Boggiano", 929 | "email": "j.boggiano@seld.be", 930 | "homepage": "http://seld.be" 931 | } 932 | ], 933 | "description": "Sends your logs to files, sockets, inboxes, databases and various web services", 934 | "homepage": "http://github.com/Seldaek/monolog", 935 | "keywords": [ 936 | "log", 937 | "logging", 938 | "psr-3" 939 | ], 940 | "time": "2016-11-26 00:15:39" 941 | }, 942 | { 943 | "name": "paragonie/random_compat", 944 | "version": "v2.0.4", 945 | "source": { 946 | "type": "git", 947 | "url": "https://github.com/paragonie/random_compat.git", 948 | "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e" 949 | }, 950 | "dist": { 951 | "type": "zip", 952 | "url": "https://api.github.com/repos/paragonie/random_compat/zipball/a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", 953 | "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", 954 | "shasum": "" 955 | }, 956 | "require": { 957 | "php": ">=5.2.0" 958 | }, 959 | "require-dev": { 960 | "phpunit/phpunit": "4.*|5.*" 961 | }, 962 | "suggest": { 963 | "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." 964 | }, 965 | "type": "library", 966 | "autoload": { 967 | "files": [ 968 | "lib/random.php" 969 | ] 970 | }, 971 | "notification-url": "https://packagist.org/downloads/", 972 | "license": [ 973 | "MIT" 974 | ], 975 | "authors": [ 976 | { 977 | "name": "Paragon Initiative Enterprises", 978 | "email": "security@paragonie.com", 979 | "homepage": "https://paragonie.com" 980 | } 981 | ], 982 | "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", 983 | "keywords": [ 984 | "csprng", 985 | "pseudorandom", 986 | "random" 987 | ], 988 | "time": "2016-11-07 23:38:38" 989 | }, 990 | { 991 | "name": "psr/log", 992 | "version": "1.0.2", 993 | "source": { 994 | "type": "git", 995 | "url": "https://github.com/php-fig/log.git", 996 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" 997 | }, 998 | "dist": { 999 | "type": "zip", 1000 | "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 1001 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 1002 | "shasum": "" 1003 | }, 1004 | "require": { 1005 | "php": ">=5.3.0" 1006 | }, 1007 | "type": "library", 1008 | "extra": { 1009 | "branch-alias": { 1010 | "dev-master": "1.0.x-dev" 1011 | } 1012 | }, 1013 | "autoload": { 1014 | "psr-4": { 1015 | "Psr\\Log\\": "Psr/Log/" 1016 | } 1017 | }, 1018 | "notification-url": "https://packagist.org/downloads/", 1019 | "license": [ 1020 | "MIT" 1021 | ], 1022 | "authors": [ 1023 | { 1024 | "name": "PHP-FIG", 1025 | "homepage": "http://www.php-fig.org/" 1026 | } 1027 | ], 1028 | "description": "Common interface for logging libraries", 1029 | "homepage": "https://github.com/php-fig/log", 1030 | "keywords": [ 1031 | "log", 1032 | "psr", 1033 | "psr-3" 1034 | ], 1035 | "time": "2016-10-10 12:19:37" 1036 | }, 1037 | { 1038 | "name": "sensio/distribution-bundle", 1039 | "version": "v5.0.18", 1040 | "source": { 1041 | "type": "git", 1042 | "url": "https://github.com/sensiolabs/SensioDistributionBundle.git", 1043 | "reference": "17846680901003d26d823c2e3ac9228702837916" 1044 | }, 1045 | "dist": { 1046 | "type": "zip", 1047 | "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/17846680901003d26d823c2e3ac9228702837916", 1048 | "reference": "17846680901003d26d823c2e3ac9228702837916", 1049 | "shasum": "" 1050 | }, 1051 | "require": { 1052 | "php": ">=5.3.9", 1053 | "sensiolabs/security-checker": "~3.0|~4.0", 1054 | "symfony/class-loader": "~2.3|~3.0", 1055 | "symfony/config": "~2.3|~3.0", 1056 | "symfony/dependency-injection": "~2.3|~3.0", 1057 | "symfony/filesystem": "~2.3|~3.0", 1058 | "symfony/http-kernel": "~2.3|~3.0", 1059 | "symfony/process": "~2.3|~3.0" 1060 | }, 1061 | "type": "symfony-bundle", 1062 | "extra": { 1063 | "branch-alias": { 1064 | "dev-master": "5.0.x-dev" 1065 | } 1066 | }, 1067 | "autoload": { 1068 | "psr-4": { 1069 | "Sensio\\Bundle\\DistributionBundle\\": "" 1070 | } 1071 | }, 1072 | "notification-url": "https://packagist.org/downloads/", 1073 | "license": [ 1074 | "MIT" 1075 | ], 1076 | "authors": [ 1077 | { 1078 | "name": "Fabien Potencier", 1079 | "email": "fabien@symfony.com" 1080 | } 1081 | ], 1082 | "description": "Base bundle for Symfony Distributions", 1083 | "keywords": [ 1084 | "configuration", 1085 | "distribution" 1086 | ], 1087 | "time": "2017-01-10 14:58:45" 1088 | }, 1089 | { 1090 | "name": "sensio/framework-extra-bundle", 1091 | "version": "v3.0.19", 1092 | "source": { 1093 | "type": "git", 1094 | "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", 1095 | "reference": "d57c2f297d17ee82baf8cae0b16dae34a9378784" 1096 | }, 1097 | "dist": { 1098 | "type": "zip", 1099 | "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/d57c2f297d17ee82baf8cae0b16dae34a9378784", 1100 | "reference": "d57c2f297d17ee82baf8cae0b16dae34a9378784", 1101 | "shasum": "" 1102 | }, 1103 | "require": { 1104 | "doctrine/common": "~2.2", 1105 | "symfony/dependency-injection": "~2.3|~3.0", 1106 | "symfony/framework-bundle": "~2.3|~3.0" 1107 | }, 1108 | "require-dev": { 1109 | "symfony/asset": "~2.7|~3.0", 1110 | "symfony/browser-kit": "~2.3|~3.0", 1111 | "symfony/dom-crawler": "~2.3|~3.0", 1112 | "symfony/expression-language": "~2.4|~3.0", 1113 | "symfony/finder": "~2.3|~3.0", 1114 | "symfony/phpunit-bridge": "~3.2", 1115 | "symfony/psr-http-message-bridge": "^0.3", 1116 | "symfony/security-bundle": "~2.4|~3.0", 1117 | "symfony/templating": "~2.3|~3.0", 1118 | "symfony/translation": "~2.3|~3.0", 1119 | "symfony/twig-bundle": "~2.3|~3.0", 1120 | "symfony/yaml": "~2.3|~3.0", 1121 | "twig/twig": "~1.11|~2.0", 1122 | "zendframework/zend-diactoros": "^1.3" 1123 | }, 1124 | "suggest": { 1125 | "symfony/expression-language": "", 1126 | "symfony/psr-http-message-bridge": "To use the PSR-7 converters", 1127 | "symfony/security-bundle": "" 1128 | }, 1129 | "type": "symfony-bundle", 1130 | "extra": { 1131 | "branch-alias": { 1132 | "dev-master": "3.0.x-dev" 1133 | } 1134 | }, 1135 | "autoload": { 1136 | "psr-4": { 1137 | "Sensio\\Bundle\\FrameworkExtraBundle\\": "" 1138 | } 1139 | }, 1140 | "notification-url": "https://packagist.org/downloads/", 1141 | "license": [ 1142 | "MIT" 1143 | ], 1144 | "authors": [ 1145 | { 1146 | "name": "Fabien Potencier", 1147 | "email": "fabien@symfony.com" 1148 | } 1149 | ], 1150 | "description": "This bundle provides a way to configure your controllers with annotations", 1151 | "keywords": [ 1152 | "annotations", 1153 | "controllers" 1154 | ], 1155 | "time": "2017-01-10 19:42:56" 1156 | }, 1157 | { 1158 | "name": "sensiolabs/security-checker", 1159 | "version": "v4.0.0", 1160 | "source": { 1161 | "type": "git", 1162 | "url": "https://github.com/sensiolabs/security-checker.git", 1163 | "reference": "116027b57b568ed61b7b1c80eeb4f6ee9e8c599c" 1164 | }, 1165 | "dist": { 1166 | "type": "zip", 1167 | "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/116027b57b568ed61b7b1c80eeb4f6ee9e8c599c", 1168 | "reference": "116027b57b568ed61b7b1c80eeb4f6ee9e8c599c", 1169 | "shasum": "" 1170 | }, 1171 | "require": { 1172 | "symfony/console": "~2.7|~3.0" 1173 | }, 1174 | "bin": [ 1175 | "security-checker" 1176 | ], 1177 | "type": "library", 1178 | "extra": { 1179 | "branch-alias": { 1180 | "dev-master": "4.0-dev" 1181 | } 1182 | }, 1183 | "autoload": { 1184 | "psr-0": { 1185 | "SensioLabs\\Security": "" 1186 | } 1187 | }, 1188 | "notification-url": "https://packagist.org/downloads/", 1189 | "license": [ 1190 | "MIT" 1191 | ], 1192 | "authors": [ 1193 | { 1194 | "name": "Fabien Potencier", 1195 | "email": "fabien.potencier@gmail.com" 1196 | } 1197 | ], 1198 | "description": "A security checker for your composer.lock", 1199 | "time": "2016-09-23 18:09:57" 1200 | }, 1201 | { 1202 | "name": "swiftmailer/swiftmailer", 1203 | "version": "v5.4.5", 1204 | "source": { 1205 | "type": "git", 1206 | "url": "https://github.com/swiftmailer/swiftmailer.git", 1207 | "reference": "cd142238a339459b10da3d8234220963f392540c" 1208 | }, 1209 | "dist": { 1210 | "type": "zip", 1211 | "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/cd142238a339459b10da3d8234220963f392540c", 1212 | "reference": "cd142238a339459b10da3d8234220963f392540c", 1213 | "shasum": "" 1214 | }, 1215 | "require": { 1216 | "php": ">=5.3.3" 1217 | }, 1218 | "require-dev": { 1219 | "mockery/mockery": "~0.9.1", 1220 | "symfony/phpunit-bridge": "~3.2" 1221 | }, 1222 | "type": "library", 1223 | "extra": { 1224 | "branch-alias": { 1225 | "dev-master": "5.4-dev" 1226 | } 1227 | }, 1228 | "autoload": { 1229 | "files": [ 1230 | "lib/swift_required.php" 1231 | ] 1232 | }, 1233 | "notification-url": "https://packagist.org/downloads/", 1234 | "license": [ 1235 | "MIT" 1236 | ], 1237 | "authors": [ 1238 | { 1239 | "name": "Chris Corbyn" 1240 | }, 1241 | { 1242 | "name": "Fabien Potencier", 1243 | "email": "fabien@symfony.com" 1244 | } 1245 | ], 1246 | "description": "Swiftmailer, free feature-rich PHP mailer", 1247 | "homepage": "http://swiftmailer.org", 1248 | "keywords": [ 1249 | "email", 1250 | "mail", 1251 | "mailer" 1252 | ], 1253 | "time": "2016-12-29 10:02:40" 1254 | }, 1255 | { 1256 | "name": "symfony/monolog-bundle", 1257 | "version": "v3.0.3", 1258 | "source": { 1259 | "type": "git", 1260 | "url": "https://github.com/symfony/monolog-bundle.git", 1261 | "reference": "ebce76a39a65495a66c34eb1574cc4b9e35a4e64" 1262 | }, 1263 | "dist": { 1264 | "type": "zip", 1265 | "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/ebce76a39a65495a66c34eb1574cc4b9e35a4e64", 1266 | "reference": "ebce76a39a65495a66c34eb1574cc4b9e35a4e64", 1267 | "shasum": "" 1268 | }, 1269 | "require": { 1270 | "monolog/monolog": "~1.22", 1271 | "php": ">=5.3.2", 1272 | "symfony/config": "~2.7|~3.0", 1273 | "symfony/dependency-injection": "~2.7|~3.0", 1274 | "symfony/http-kernel": "~2.7|~3.0", 1275 | "symfony/monolog-bridge": "~2.7|~3.0" 1276 | }, 1277 | "require-dev": { 1278 | "phpunit/phpunit": "^4.8", 1279 | "symfony/console": "~2.3|~3.0", 1280 | "symfony/yaml": "~2.3|~3.0" 1281 | }, 1282 | "type": "symfony-bundle", 1283 | "extra": { 1284 | "branch-alias": { 1285 | "dev-master": "3.x-dev" 1286 | } 1287 | }, 1288 | "autoload": { 1289 | "psr-4": { 1290 | "Symfony\\Bundle\\MonologBundle\\": "" 1291 | } 1292 | }, 1293 | "notification-url": "https://packagist.org/downloads/", 1294 | "license": [ 1295 | "MIT" 1296 | ], 1297 | "authors": [ 1298 | { 1299 | "name": "Symfony Community", 1300 | "homepage": "http://symfony.com/contributors" 1301 | }, 1302 | { 1303 | "name": "Fabien Potencier", 1304 | "email": "fabien@symfony.com" 1305 | } 1306 | ], 1307 | "description": "Symfony MonologBundle", 1308 | "homepage": "http://symfony.com", 1309 | "keywords": [ 1310 | "log", 1311 | "logging" 1312 | ], 1313 | "time": "2017-01-10 20:01:51" 1314 | }, 1315 | { 1316 | "name": "symfony/polyfill-apcu", 1317 | "version": "v1.3.0", 1318 | "source": { 1319 | "type": "git", 1320 | "url": "https://github.com/symfony/polyfill-apcu.git", 1321 | "reference": "5d4474f447403c3348e37b70acc2b95475b7befa" 1322 | }, 1323 | "dist": { 1324 | "type": "zip", 1325 | "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/5d4474f447403c3348e37b70acc2b95475b7befa", 1326 | "reference": "5d4474f447403c3348e37b70acc2b95475b7befa", 1327 | "shasum": "" 1328 | }, 1329 | "require": { 1330 | "php": ">=5.3.3" 1331 | }, 1332 | "type": "library", 1333 | "extra": { 1334 | "branch-alias": { 1335 | "dev-master": "1.3-dev" 1336 | } 1337 | }, 1338 | "autoload": { 1339 | "files": [ 1340 | "bootstrap.php" 1341 | ] 1342 | }, 1343 | "notification-url": "https://packagist.org/downloads/", 1344 | "license": [ 1345 | "MIT" 1346 | ], 1347 | "authors": [ 1348 | { 1349 | "name": "Nicolas Grekas", 1350 | "email": "p@tchwork.com" 1351 | }, 1352 | { 1353 | "name": "Symfony Community", 1354 | "homepage": "https://symfony.com/contributors" 1355 | } 1356 | ], 1357 | "description": "Symfony polyfill backporting apcu_* functions to lower PHP versions", 1358 | "homepage": "https://symfony.com", 1359 | "keywords": [ 1360 | "apcu", 1361 | "compatibility", 1362 | "polyfill", 1363 | "portable", 1364 | "shim" 1365 | ], 1366 | "time": "2016-11-14 01:06:16" 1367 | }, 1368 | { 1369 | "name": "symfony/polyfill-intl-icu", 1370 | "version": "v1.3.0", 1371 | "source": { 1372 | "type": "git", 1373 | "url": "https://github.com/symfony/polyfill-intl-icu.git", 1374 | "reference": "2d6e2b20d457603eefb6e614286c22efca30fdb4" 1375 | }, 1376 | "dist": { 1377 | "type": "zip", 1378 | "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/2d6e2b20d457603eefb6e614286c22efca30fdb4", 1379 | "reference": "2d6e2b20d457603eefb6e614286c22efca30fdb4", 1380 | "shasum": "" 1381 | }, 1382 | "require": { 1383 | "php": ">=5.3.3", 1384 | "symfony/intl": "~2.3|~3.0" 1385 | }, 1386 | "suggest": { 1387 | "ext-intl": "For best performance" 1388 | }, 1389 | "type": "library", 1390 | "extra": { 1391 | "branch-alias": { 1392 | "dev-master": "1.3-dev" 1393 | } 1394 | }, 1395 | "autoload": { 1396 | "files": [ 1397 | "bootstrap.php" 1398 | ] 1399 | }, 1400 | "notification-url": "https://packagist.org/downloads/", 1401 | "license": [ 1402 | "MIT" 1403 | ], 1404 | "authors": [ 1405 | { 1406 | "name": "Nicolas Grekas", 1407 | "email": "p@tchwork.com" 1408 | }, 1409 | { 1410 | "name": "Symfony Community", 1411 | "homepage": "https://symfony.com/contributors" 1412 | } 1413 | ], 1414 | "description": "Symfony polyfill for intl's ICU-related data and classes", 1415 | "homepage": "https://symfony.com", 1416 | "keywords": [ 1417 | "compatibility", 1418 | "icu", 1419 | "intl", 1420 | "polyfill", 1421 | "portable", 1422 | "shim" 1423 | ], 1424 | "time": "2016-11-14 01:06:16" 1425 | }, 1426 | { 1427 | "name": "symfony/polyfill-mbstring", 1428 | "version": "v1.3.0", 1429 | "source": { 1430 | "type": "git", 1431 | "url": "https://github.com/symfony/polyfill-mbstring.git", 1432 | "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" 1433 | }, 1434 | "dist": { 1435 | "type": "zip", 1436 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", 1437 | "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", 1438 | "shasum": "" 1439 | }, 1440 | "require": { 1441 | "php": ">=5.3.3" 1442 | }, 1443 | "suggest": { 1444 | "ext-mbstring": "For best performance" 1445 | }, 1446 | "type": "library", 1447 | "extra": { 1448 | "branch-alias": { 1449 | "dev-master": "1.3-dev" 1450 | } 1451 | }, 1452 | "autoload": { 1453 | "psr-4": { 1454 | "Symfony\\Polyfill\\Mbstring\\": "" 1455 | }, 1456 | "files": [ 1457 | "bootstrap.php" 1458 | ] 1459 | }, 1460 | "notification-url": "https://packagist.org/downloads/", 1461 | "license": [ 1462 | "MIT" 1463 | ], 1464 | "authors": [ 1465 | { 1466 | "name": "Nicolas Grekas", 1467 | "email": "p@tchwork.com" 1468 | }, 1469 | { 1470 | "name": "Symfony Community", 1471 | "homepage": "https://symfony.com/contributors" 1472 | } 1473 | ], 1474 | "description": "Symfony polyfill for the Mbstring extension", 1475 | "homepage": "https://symfony.com", 1476 | "keywords": [ 1477 | "compatibility", 1478 | "mbstring", 1479 | "polyfill", 1480 | "portable", 1481 | "shim" 1482 | ], 1483 | "time": "2016-11-14 01:06:16" 1484 | }, 1485 | { 1486 | "name": "symfony/polyfill-php54", 1487 | "version": "v1.3.0", 1488 | "source": { 1489 | "type": "git", 1490 | "url": "https://github.com/symfony/polyfill-php54.git", 1491 | "reference": "90e085822963fdcc9d1c5b73deb3d2e5783b16a0" 1492 | }, 1493 | "dist": { 1494 | "type": "zip", 1495 | "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/90e085822963fdcc9d1c5b73deb3d2e5783b16a0", 1496 | "reference": "90e085822963fdcc9d1c5b73deb3d2e5783b16a0", 1497 | "shasum": "" 1498 | }, 1499 | "require": { 1500 | "php": ">=5.3.3" 1501 | }, 1502 | "type": "library", 1503 | "extra": { 1504 | "branch-alias": { 1505 | "dev-master": "1.3-dev" 1506 | } 1507 | }, 1508 | "autoload": { 1509 | "psr-4": { 1510 | "Symfony\\Polyfill\\Php54\\": "" 1511 | }, 1512 | "files": [ 1513 | "bootstrap.php" 1514 | ], 1515 | "classmap": [ 1516 | "Resources/stubs" 1517 | ] 1518 | }, 1519 | "notification-url": "https://packagist.org/downloads/", 1520 | "license": [ 1521 | "MIT" 1522 | ], 1523 | "authors": [ 1524 | { 1525 | "name": "Nicolas Grekas", 1526 | "email": "p@tchwork.com" 1527 | }, 1528 | { 1529 | "name": "Symfony Community", 1530 | "homepage": "https://symfony.com/contributors" 1531 | } 1532 | ], 1533 | "description": "Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions", 1534 | "homepage": "https://symfony.com", 1535 | "keywords": [ 1536 | "compatibility", 1537 | "polyfill", 1538 | "portable", 1539 | "shim" 1540 | ], 1541 | "time": "2016-11-14 01:06:16" 1542 | }, 1543 | { 1544 | "name": "symfony/polyfill-php55", 1545 | "version": "v1.3.0", 1546 | "source": { 1547 | "type": "git", 1548 | "url": "https://github.com/symfony/polyfill-php55.git", 1549 | "reference": "03e3f0350bca2220e3623a0e340eef194405fc67" 1550 | }, 1551 | "dist": { 1552 | "type": "zip", 1553 | "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/03e3f0350bca2220e3623a0e340eef194405fc67", 1554 | "reference": "03e3f0350bca2220e3623a0e340eef194405fc67", 1555 | "shasum": "" 1556 | }, 1557 | "require": { 1558 | "ircmaxell/password-compat": "~1.0", 1559 | "php": ">=5.3.3" 1560 | }, 1561 | "type": "library", 1562 | "extra": { 1563 | "branch-alias": { 1564 | "dev-master": "1.3-dev" 1565 | } 1566 | }, 1567 | "autoload": { 1568 | "psr-4": { 1569 | "Symfony\\Polyfill\\Php55\\": "" 1570 | }, 1571 | "files": [ 1572 | "bootstrap.php" 1573 | ] 1574 | }, 1575 | "notification-url": "https://packagist.org/downloads/", 1576 | "license": [ 1577 | "MIT" 1578 | ], 1579 | "authors": [ 1580 | { 1581 | "name": "Nicolas Grekas", 1582 | "email": "p@tchwork.com" 1583 | }, 1584 | { 1585 | "name": "Symfony Community", 1586 | "homepage": "https://symfony.com/contributors" 1587 | } 1588 | ], 1589 | "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions", 1590 | "homepage": "https://symfony.com", 1591 | "keywords": [ 1592 | "compatibility", 1593 | "polyfill", 1594 | "portable", 1595 | "shim" 1596 | ], 1597 | "time": "2016-11-14 01:06:16" 1598 | }, 1599 | { 1600 | "name": "symfony/polyfill-php56", 1601 | "version": "v1.3.0", 1602 | "source": { 1603 | "type": "git", 1604 | "url": "https://github.com/symfony/polyfill-php56.git", 1605 | "reference": "1dd42b9b89556f18092f3d1ada22cb05ac85383c" 1606 | }, 1607 | "dist": { 1608 | "type": "zip", 1609 | "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/1dd42b9b89556f18092f3d1ada22cb05ac85383c", 1610 | "reference": "1dd42b9b89556f18092f3d1ada22cb05ac85383c", 1611 | "shasum": "" 1612 | }, 1613 | "require": { 1614 | "php": ">=5.3.3", 1615 | "symfony/polyfill-util": "~1.0" 1616 | }, 1617 | "type": "library", 1618 | "extra": { 1619 | "branch-alias": { 1620 | "dev-master": "1.3-dev" 1621 | } 1622 | }, 1623 | "autoload": { 1624 | "psr-4": { 1625 | "Symfony\\Polyfill\\Php56\\": "" 1626 | }, 1627 | "files": [ 1628 | "bootstrap.php" 1629 | ] 1630 | }, 1631 | "notification-url": "https://packagist.org/downloads/", 1632 | "license": [ 1633 | "MIT" 1634 | ], 1635 | "authors": [ 1636 | { 1637 | "name": "Nicolas Grekas", 1638 | "email": "p@tchwork.com" 1639 | }, 1640 | { 1641 | "name": "Symfony Community", 1642 | "homepage": "https://symfony.com/contributors" 1643 | } 1644 | ], 1645 | "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", 1646 | "homepage": "https://symfony.com", 1647 | "keywords": [ 1648 | "compatibility", 1649 | "polyfill", 1650 | "portable", 1651 | "shim" 1652 | ], 1653 | "time": "2016-11-14 01:06:16" 1654 | }, 1655 | { 1656 | "name": "symfony/polyfill-php70", 1657 | "version": "v1.3.0", 1658 | "source": { 1659 | "type": "git", 1660 | "url": "https://github.com/symfony/polyfill-php70.git", 1661 | "reference": "13ce343935f0f91ca89605a2f6ca6f5c2f3faac2" 1662 | }, 1663 | "dist": { 1664 | "type": "zip", 1665 | "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/13ce343935f0f91ca89605a2f6ca6f5c2f3faac2", 1666 | "reference": "13ce343935f0f91ca89605a2f6ca6f5c2f3faac2", 1667 | "shasum": "" 1668 | }, 1669 | "require": { 1670 | "paragonie/random_compat": "~1.0|~2.0", 1671 | "php": ">=5.3.3" 1672 | }, 1673 | "type": "library", 1674 | "extra": { 1675 | "branch-alias": { 1676 | "dev-master": "1.3-dev" 1677 | } 1678 | }, 1679 | "autoload": { 1680 | "psr-4": { 1681 | "Symfony\\Polyfill\\Php70\\": "" 1682 | }, 1683 | "files": [ 1684 | "bootstrap.php" 1685 | ], 1686 | "classmap": [ 1687 | "Resources/stubs" 1688 | ] 1689 | }, 1690 | "notification-url": "https://packagist.org/downloads/", 1691 | "license": [ 1692 | "MIT" 1693 | ], 1694 | "authors": [ 1695 | { 1696 | "name": "Nicolas Grekas", 1697 | "email": "p@tchwork.com" 1698 | }, 1699 | { 1700 | "name": "Symfony Community", 1701 | "homepage": "https://symfony.com/contributors" 1702 | } 1703 | ], 1704 | "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", 1705 | "homepage": "https://symfony.com", 1706 | "keywords": [ 1707 | "compatibility", 1708 | "polyfill", 1709 | "portable", 1710 | "shim" 1711 | ], 1712 | "time": "2016-11-14 01:06:16" 1713 | }, 1714 | { 1715 | "name": "symfony/polyfill-util", 1716 | "version": "v1.3.0", 1717 | "source": { 1718 | "type": "git", 1719 | "url": "https://github.com/symfony/polyfill-util.git", 1720 | "reference": "746bce0fca664ac0a575e465f65c6643faddf7fb" 1721 | }, 1722 | "dist": { 1723 | "type": "zip", 1724 | "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/746bce0fca664ac0a575e465f65c6643faddf7fb", 1725 | "reference": "746bce0fca664ac0a575e465f65c6643faddf7fb", 1726 | "shasum": "" 1727 | }, 1728 | "require": { 1729 | "php": ">=5.3.3" 1730 | }, 1731 | "type": "library", 1732 | "extra": { 1733 | "branch-alias": { 1734 | "dev-master": "1.3-dev" 1735 | } 1736 | }, 1737 | "autoload": { 1738 | "psr-4": { 1739 | "Symfony\\Polyfill\\Util\\": "" 1740 | } 1741 | }, 1742 | "notification-url": "https://packagist.org/downloads/", 1743 | "license": [ 1744 | "MIT" 1745 | ], 1746 | "authors": [ 1747 | { 1748 | "name": "Nicolas Grekas", 1749 | "email": "p@tchwork.com" 1750 | }, 1751 | { 1752 | "name": "Symfony Community", 1753 | "homepage": "https://symfony.com/contributors" 1754 | } 1755 | ], 1756 | "description": "Symfony utilities for portability of PHP codes", 1757 | "homepage": "https://symfony.com", 1758 | "keywords": [ 1759 | "compat", 1760 | "compatibility", 1761 | "polyfill", 1762 | "shim" 1763 | ], 1764 | "time": "2016-11-14 01:06:16" 1765 | }, 1766 | { 1767 | "name": "symfony/security-acl", 1768 | "version": "v2.8.0", 1769 | "source": { 1770 | "type": "git", 1771 | "url": "https://github.com/symfony/security-acl.git", 1772 | "reference": "4a3f7327ad215242c78f6564ad4ea6d2db1b8347" 1773 | }, 1774 | "dist": { 1775 | "type": "zip", 1776 | "url": "https://api.github.com/repos/symfony/security-acl/zipball/4a3f7327ad215242c78f6564ad4ea6d2db1b8347", 1777 | "reference": "4a3f7327ad215242c78f6564ad4ea6d2db1b8347", 1778 | "shasum": "" 1779 | }, 1780 | "require": { 1781 | "php": ">=5.3.9", 1782 | "symfony/security-core": "~2.4|~3.0.0" 1783 | }, 1784 | "require-dev": { 1785 | "doctrine/common": "~2.2", 1786 | "doctrine/dbal": "~2.2", 1787 | "psr/log": "~1.0", 1788 | "symfony/phpunit-bridge": "~2.7|~3.0.0" 1789 | }, 1790 | "suggest": { 1791 | "doctrine/dbal": "For using the built-in ACL implementation", 1792 | "symfony/class-loader": "For using the ACL generateSql script", 1793 | "symfony/finder": "For using the ACL generateSql script" 1794 | }, 1795 | "type": "library", 1796 | "extra": { 1797 | "branch-alias": { 1798 | "dev-master": "2.8-dev" 1799 | } 1800 | }, 1801 | "autoload": { 1802 | "psr-4": { 1803 | "Symfony\\Component\\Security\\Acl\\": "" 1804 | }, 1805 | "exclude-from-classmap": [ 1806 | "/Tests/" 1807 | ] 1808 | }, 1809 | "notification-url": "https://packagist.org/downloads/", 1810 | "license": [ 1811 | "MIT" 1812 | ], 1813 | "authors": [ 1814 | { 1815 | "name": "Fabien Potencier", 1816 | "email": "fabien@symfony.com" 1817 | }, 1818 | { 1819 | "name": "Symfony Community", 1820 | "homepage": "https://symfony.com/contributors" 1821 | } 1822 | ], 1823 | "description": "Symfony Security Component - ACL (Access Control List)", 1824 | "homepage": "https://symfony.com", 1825 | "time": "2015-12-28 09:39:09" 1826 | }, 1827 | { 1828 | "name": "symfony/swiftmailer-bundle", 1829 | "version": "v2.4.2", 1830 | "source": { 1831 | "type": "git", 1832 | "url": "https://github.com/symfony/swiftmailer-bundle.git", 1833 | "reference": "ad751095576ce0c12a284e30e3fff80c91f27225" 1834 | }, 1835 | "dist": { 1836 | "type": "zip", 1837 | "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/ad751095576ce0c12a284e30e3fff80c91f27225", 1838 | "reference": "ad751095576ce0c12a284e30e3fff80c91f27225", 1839 | "shasum": "" 1840 | }, 1841 | "require": { 1842 | "php": ">=5.3.2", 1843 | "swiftmailer/swiftmailer": ">=4.2.0,~5.0", 1844 | "symfony/config": "~2.7|~3.0", 1845 | "symfony/dependency-injection": "~2.7|~3.0", 1846 | "symfony/http-kernel": "~2.7|~3.0" 1847 | }, 1848 | "require-dev": { 1849 | "symfony/console": "~2.7|~3.0", 1850 | "symfony/framework-bundle": "~2.7|~3.0", 1851 | "symfony/phpunit-bridge": "~2.7|~3.0", 1852 | "symfony/yaml": "~2.7|~3.0" 1853 | }, 1854 | "suggest": { 1855 | "psr/log": "Allows logging" 1856 | }, 1857 | "type": "symfony-bundle", 1858 | "extra": { 1859 | "branch-alias": { 1860 | "dev-master": "2.4-dev" 1861 | } 1862 | }, 1863 | "autoload": { 1864 | "psr-4": { 1865 | "Symfony\\Bundle\\SwiftmailerBundle\\": "" 1866 | } 1867 | }, 1868 | "notification-url": "https://packagist.org/downloads/", 1869 | "license": [ 1870 | "MIT" 1871 | ], 1872 | "authors": [ 1873 | { 1874 | "name": "Symfony Community", 1875 | "homepage": "http://symfony.com/contributors" 1876 | }, 1877 | { 1878 | "name": "Fabien Potencier", 1879 | "email": "fabien@symfony.com" 1880 | } 1881 | ], 1882 | "description": "Symfony SwiftmailerBundle", 1883 | "homepage": "http://symfony.com", 1884 | "time": "2016-12-20 04:44:33" 1885 | }, 1886 | { 1887 | "name": "symfony/symfony", 1888 | "version": "v2.8.16", 1889 | "source": { 1890 | "type": "git", 1891 | "url": "https://github.com/symfony/symfony.git", 1892 | "reference": "9fef72a3ab561c4bfa703a70369db028dec387d2" 1893 | }, 1894 | "dist": { 1895 | "type": "zip", 1896 | "url": "https://api.github.com/repos/symfony/symfony/zipball/9fef72a3ab561c4bfa703a70369db028dec387d2", 1897 | "reference": "9fef72a3ab561c4bfa703a70369db028dec387d2", 1898 | "shasum": "" 1899 | }, 1900 | "require": { 1901 | "doctrine/common": "~2.4", 1902 | "php": ">=5.3.9", 1903 | "psr/log": "~1.0", 1904 | "symfony/polyfill-apcu": "~1.1", 1905 | "symfony/polyfill-intl-icu": "~1.0", 1906 | "symfony/polyfill-mbstring": "~1.0", 1907 | "symfony/polyfill-php54": "~1.0", 1908 | "symfony/polyfill-php55": "~1.0", 1909 | "symfony/polyfill-php56": "~1.0", 1910 | "symfony/polyfill-php70": "~1.0", 1911 | "symfony/polyfill-util": "~1.0", 1912 | "symfony/security-acl": "~2.7|~3.0.0", 1913 | "twig/twig": "~1.28|~2.0" 1914 | }, 1915 | "conflict": { 1916 | "phpdocumentor/reflection": "<1.0.7" 1917 | }, 1918 | "replace": { 1919 | "symfony/asset": "self.version", 1920 | "symfony/browser-kit": "self.version", 1921 | "symfony/class-loader": "self.version", 1922 | "symfony/config": "self.version", 1923 | "symfony/console": "self.version", 1924 | "symfony/css-selector": "self.version", 1925 | "symfony/debug": "self.version", 1926 | "symfony/debug-bundle": "self.version", 1927 | "symfony/dependency-injection": "self.version", 1928 | "symfony/doctrine-bridge": "self.version", 1929 | "symfony/dom-crawler": "self.version", 1930 | "symfony/event-dispatcher": "self.version", 1931 | "symfony/expression-language": "self.version", 1932 | "symfony/filesystem": "self.version", 1933 | "symfony/finder": "self.version", 1934 | "symfony/form": "self.version", 1935 | "symfony/framework-bundle": "self.version", 1936 | "symfony/http-foundation": "self.version", 1937 | "symfony/http-kernel": "self.version", 1938 | "symfony/intl": "self.version", 1939 | "symfony/ldap": "self.version", 1940 | "symfony/locale": "self.version", 1941 | "symfony/monolog-bridge": "self.version", 1942 | "symfony/options-resolver": "self.version", 1943 | "symfony/process": "self.version", 1944 | "symfony/property-access": "self.version", 1945 | "symfony/property-info": "self.version", 1946 | "symfony/proxy-manager-bridge": "self.version", 1947 | "symfony/routing": "self.version", 1948 | "symfony/security": "self.version", 1949 | "symfony/security-bundle": "self.version", 1950 | "symfony/security-core": "self.version", 1951 | "symfony/security-csrf": "self.version", 1952 | "symfony/security-guard": "self.version", 1953 | "symfony/security-http": "self.version", 1954 | "symfony/serializer": "self.version", 1955 | "symfony/stopwatch": "self.version", 1956 | "symfony/swiftmailer-bridge": "self.version", 1957 | "symfony/templating": "self.version", 1958 | "symfony/translation": "self.version", 1959 | "symfony/twig-bridge": "self.version", 1960 | "symfony/twig-bundle": "self.version", 1961 | "symfony/validator": "self.version", 1962 | "symfony/var-dumper": "self.version", 1963 | "symfony/web-profiler-bundle": "self.version", 1964 | "symfony/yaml": "self.version" 1965 | }, 1966 | "require-dev": { 1967 | "doctrine/data-fixtures": "1.0.*", 1968 | "doctrine/dbal": "~2.4", 1969 | "doctrine/doctrine-bundle": "~1.2", 1970 | "doctrine/orm": "~2.4,>=2.4.5", 1971 | "egulias/email-validator": "~1.2,>=1.2.1", 1972 | "monolog/monolog": "~1.11", 1973 | "ocramius/proxy-manager": "~0.4|~1.0|~2.0", 1974 | "phpdocumentor/reflection": "^1.0.7", 1975 | "symfony/phpunit-bridge": "~3.2" 1976 | }, 1977 | "type": "library", 1978 | "extra": { 1979 | "branch-alias": { 1980 | "dev-master": "2.8-dev" 1981 | } 1982 | }, 1983 | "autoload": { 1984 | "psr-4": { 1985 | "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/", 1986 | "Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/", 1987 | "Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/", 1988 | "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/", 1989 | "Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/", 1990 | "Symfony\\Bundle\\": "src/Symfony/Bundle/", 1991 | "Symfony\\Component\\": "src/Symfony/Component/" 1992 | }, 1993 | "classmap": [ 1994 | "src/Symfony/Component/Intl/Resources/stubs" 1995 | ], 1996 | "exclude-from-classmap": [ 1997 | "**/Tests/" 1998 | ] 1999 | }, 2000 | "notification-url": "https://packagist.org/downloads/", 2001 | "license": [ 2002 | "MIT" 2003 | ], 2004 | "authors": [ 2005 | { 2006 | "name": "Fabien Potencier", 2007 | "email": "fabien@symfony.com" 2008 | }, 2009 | { 2010 | "name": "Symfony Community", 2011 | "homepage": "https://symfony.com/contributors" 2012 | } 2013 | ], 2014 | "description": "The Symfony PHP framework", 2015 | "homepage": "https://symfony.com", 2016 | "keywords": [ 2017 | "framework" 2018 | ], 2019 | "time": "2017-01-12 20:27:46" 2020 | }, 2021 | { 2022 | "name": "twig/twig", 2023 | "version": "v1.31.0", 2024 | "source": { 2025 | "type": "git", 2026 | "url": "https://github.com/twigphp/Twig.git", 2027 | "reference": "ddc9e3e20ee9c0b6908f401ac8353635b750eca7" 2028 | }, 2029 | "dist": { 2030 | "type": "zip", 2031 | "url": "https://api.github.com/repos/twigphp/Twig/zipball/ddc9e3e20ee9c0b6908f401ac8353635b750eca7", 2032 | "reference": "ddc9e3e20ee9c0b6908f401ac8353635b750eca7", 2033 | "shasum": "" 2034 | }, 2035 | "require": { 2036 | "php": ">=5.2.7" 2037 | }, 2038 | "require-dev": { 2039 | "symfony/debug": "~2.7", 2040 | "symfony/phpunit-bridge": "~3.2" 2041 | }, 2042 | "type": "library", 2043 | "extra": { 2044 | "branch-alias": { 2045 | "dev-master": "1.31-dev" 2046 | } 2047 | }, 2048 | "autoload": { 2049 | "psr-0": { 2050 | "Twig_": "lib/" 2051 | } 2052 | }, 2053 | "notification-url": "https://packagist.org/downloads/", 2054 | "license": [ 2055 | "BSD-3-Clause" 2056 | ], 2057 | "authors": [ 2058 | { 2059 | "name": "Fabien Potencier", 2060 | "email": "fabien@symfony.com", 2061 | "homepage": "http://fabien.potencier.org", 2062 | "role": "Lead Developer" 2063 | }, 2064 | { 2065 | "name": "Armin Ronacher", 2066 | "email": "armin.ronacher@active-4.com", 2067 | "role": "Project Founder" 2068 | }, 2069 | { 2070 | "name": "Twig Team", 2071 | "homepage": "http://twig.sensiolabs.org/contributors", 2072 | "role": "Contributors" 2073 | } 2074 | ], 2075 | "description": "Twig, the flexible, fast, and secure template language for PHP", 2076 | "homepage": "http://twig.sensiolabs.org", 2077 | "keywords": [ 2078 | "templating" 2079 | ], 2080 | "time": "2017-01-11 19:36:15" 2081 | } 2082 | ], 2083 | "packages-dev": [ 2084 | { 2085 | "name": "doctrine/data-fixtures", 2086 | "version": "v1.2.2", 2087 | "source": { 2088 | "type": "git", 2089 | "url": "https://github.com/doctrine/data-fixtures.git", 2090 | "reference": "17fa5bfe6ff52e35cb3d9ec37c934a2f4bd1fa2e" 2091 | }, 2092 | "dist": { 2093 | "type": "zip", 2094 | "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/17fa5bfe6ff52e35cb3d9ec37c934a2f4bd1fa2e", 2095 | "reference": "17fa5bfe6ff52e35cb3d9ec37c934a2f4bd1fa2e", 2096 | "shasum": "" 2097 | }, 2098 | "require": { 2099 | "doctrine/common": "~2.2", 2100 | "php": "^5.6 || ^7.0" 2101 | }, 2102 | "conflict": { 2103 | "doctrine/orm": "< 2.4" 2104 | }, 2105 | "require-dev": { 2106 | "doctrine/dbal": "^2.5.4", 2107 | "doctrine/orm": "^2.5.4", 2108 | "phpunit/phpunit": "^5.4.6" 2109 | }, 2110 | "suggest": { 2111 | "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", 2112 | "doctrine/orm": "For loading ORM fixtures", 2113 | "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" 2114 | }, 2115 | "type": "library", 2116 | "extra": { 2117 | "branch-alias": { 2118 | "dev-master": "1.3.x-dev" 2119 | } 2120 | }, 2121 | "autoload": { 2122 | "psr-0": { 2123 | "Doctrine\\Common\\DataFixtures": "lib/" 2124 | } 2125 | }, 2126 | "notification-url": "https://packagist.org/downloads/", 2127 | "license": [ 2128 | "MIT" 2129 | ], 2130 | "authors": [ 2131 | { 2132 | "name": "Jonathan Wage", 2133 | "email": "jonwage@gmail.com" 2134 | } 2135 | ], 2136 | "description": "Data Fixtures for all Doctrine Object Managers", 2137 | "homepage": "http://www.doctrine-project.org", 2138 | "keywords": [ 2139 | "database" 2140 | ], 2141 | "time": "2016-09-20 10:07:57" 2142 | }, 2143 | { 2144 | "name": "doctrine/doctrine-fixtures-bundle", 2145 | "version": "2.3.0", 2146 | "source": { 2147 | "type": "git", 2148 | "url": "https://github.com/doctrine/DoctrineFixturesBundle.git", 2149 | "reference": "0f1a2f91b349e10f5c343f75ab71d23aace5b029" 2150 | }, 2151 | "dist": { 2152 | "type": "zip", 2153 | "url": "https://api.github.com/repos/doctrine/DoctrineFixturesBundle/zipball/0f1a2f91b349e10f5c343f75ab71d23aace5b029", 2154 | "reference": "0f1a2f91b349e10f5c343f75ab71d23aace5b029", 2155 | "shasum": "" 2156 | }, 2157 | "require": { 2158 | "doctrine/data-fixtures": "~1.0", 2159 | "doctrine/doctrine-bundle": "~1.0", 2160 | "php": ">=5.3.2", 2161 | "symfony/doctrine-bridge": "~2.3|~3.0" 2162 | }, 2163 | "type": "symfony-bundle", 2164 | "extra": { 2165 | "branch-alias": { 2166 | "dev-master": "2.2.x-dev" 2167 | } 2168 | }, 2169 | "autoload": { 2170 | "psr-4": { 2171 | "Doctrine\\Bundle\\FixturesBundle\\": "" 2172 | } 2173 | }, 2174 | "notification-url": "https://packagist.org/downloads/", 2175 | "license": [ 2176 | "MIT" 2177 | ], 2178 | "authors": [ 2179 | { 2180 | "name": "Symfony Community", 2181 | "homepage": "http://symfony.com/contributors" 2182 | }, 2183 | { 2184 | "name": "Doctrine Project", 2185 | "homepage": "http://www.doctrine-project.org" 2186 | }, 2187 | { 2188 | "name": "Fabien Potencier", 2189 | "email": "fabien@symfony.com" 2190 | } 2191 | ], 2192 | "description": "Symfony DoctrineFixturesBundle", 2193 | "homepage": "http://www.doctrine-project.org", 2194 | "keywords": [ 2195 | "Fixture", 2196 | "persistence" 2197 | ], 2198 | "time": "2015-11-04 21:23:23" 2199 | }, 2200 | { 2201 | "name": "sensio/generator-bundle", 2202 | "version": "v3.1.2", 2203 | "source": { 2204 | "type": "git", 2205 | "url": "https://github.com/sensiolabs/SensioGeneratorBundle.git", 2206 | "reference": "ec278c0bd530edf155c4a00900577b5cb80f559e" 2207 | }, 2208 | "dist": { 2209 | "type": "zip", 2210 | "url": "https://api.github.com/repos/sensiolabs/SensioGeneratorBundle/zipball/ec278c0bd530edf155c4a00900577b5cb80f559e", 2211 | "reference": "ec278c0bd530edf155c4a00900577b5cb80f559e", 2212 | "shasum": "" 2213 | }, 2214 | "require": { 2215 | "symfony/console": "~2.7|~3.0", 2216 | "symfony/framework-bundle": "~2.7|~3.0", 2217 | "symfony/process": "~2.7|~3.0", 2218 | "symfony/yaml": "~2.7|~3.0", 2219 | "twig/twig": "^1.28.2|^2.0" 2220 | }, 2221 | "require-dev": { 2222 | "doctrine/orm": "~2.4", 2223 | "symfony/doctrine-bridge": "~2.7|~3.0" 2224 | }, 2225 | "type": "symfony-bundle", 2226 | "extra": { 2227 | "branch-alias": { 2228 | "dev-master": "3.1.x-dev" 2229 | } 2230 | }, 2231 | "autoload": { 2232 | "psr-4": { 2233 | "Sensio\\Bundle\\GeneratorBundle\\": "" 2234 | }, 2235 | "exclude-from-classmap": [ 2236 | "/Tests/" 2237 | ] 2238 | }, 2239 | "notification-url": "https://packagist.org/downloads/", 2240 | "license": [ 2241 | "MIT" 2242 | ], 2243 | "authors": [ 2244 | { 2245 | "name": "Fabien Potencier", 2246 | "email": "fabien@symfony.com" 2247 | } 2248 | ], 2249 | "description": "This bundle generates code for you", 2250 | "time": "2016-12-05 16:01:19" 2251 | }, 2252 | { 2253 | "name": "symfony/phpunit-bridge", 2254 | "version": "v2.8.16", 2255 | "source": { 2256 | "type": "git", 2257 | "url": "https://github.com/symfony/phpunit-bridge.git", 2258 | "reference": "1e76e80da601c6969336309678a0af4018abcae7" 2259 | }, 2260 | "dist": { 2261 | "type": "zip", 2262 | "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/1e76e80da601c6969336309678a0af4018abcae7", 2263 | "reference": "1e76e80da601c6969336309678a0af4018abcae7", 2264 | "shasum": "" 2265 | }, 2266 | "require": { 2267 | "php": ">=5.3.3" 2268 | }, 2269 | "suggest": { 2270 | "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" 2271 | }, 2272 | "type": "symfony-bridge", 2273 | "extra": { 2274 | "branch-alias": { 2275 | "dev-master": "2.8-dev" 2276 | } 2277 | }, 2278 | "autoload": { 2279 | "files": [ 2280 | "bootstrap.php" 2281 | ], 2282 | "psr-4": { 2283 | "Symfony\\Bridge\\PhpUnit\\": "" 2284 | }, 2285 | "exclude-from-classmap": [ 2286 | "/Tests/" 2287 | ] 2288 | }, 2289 | "notification-url": "https://packagist.org/downloads/", 2290 | "license": [ 2291 | "MIT" 2292 | ], 2293 | "authors": [ 2294 | { 2295 | "name": "Nicolas Grekas", 2296 | "email": "p@tchwork.com" 2297 | }, 2298 | { 2299 | "name": "Symfony Community", 2300 | "homepage": "https://symfony.com/contributors" 2301 | } 2302 | ], 2303 | "description": "Symfony PHPUnit Bridge", 2304 | "homepage": "https://symfony.com", 2305 | "time": "2017-01-02 20:30:24" 2306 | } 2307 | ], 2308 | "aliases": [], 2309 | "minimum-stability": "stable", 2310 | "stability-flags": [], 2311 | "prefer-stable": false, 2312 | "prefer-lowest": false, 2313 | "platform": { 2314 | "php": ">=5.3.9" 2315 | }, 2316 | "platform-dev": [] 2317 | } 2318 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uirapuru/multidb/0953d48083cb3374633a982d8ba6ae6a928c0c3f/screenshot.png -------------------------------------------------------------------------------- /src/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Require all denied 3 | 4 | 5 | Order deny,allow 6 | Deny from all 7 | 8 | -------------------------------------------------------------------------------- /src/AppBundle/AppBundle.php: -------------------------------------------------------------------------------- 1 | defaultConnection = $defaultConnection; 26 | $this->tenantConnection = $tenantConnection; 27 | } 28 | 29 | /** 30 | * @return string 31 | */ 32 | public function getDefault() 33 | { 34 | return $this->data['default']; 35 | } 36 | 37 | /** 38 | * @return string 39 | */ 40 | public function getTenant() 41 | { 42 | return $this->data['tenant']; 43 | } 44 | 45 | /** 46 | * @param Request $request 47 | * @param Response $response 48 | * @param Exception|null $exception 49 | */ 50 | public function collect(Request $request, Response $response, Exception $exception = null) 51 | { 52 | $this->data['default'] = $this->defaultConnection->getDatabase(); 53 | $this->data['tenant'] = $this->tenantConnection->isConnected() ? $this->tenantConnection->getDatabase() : null; 54 | } 55 | 56 | /** 57 | * @return string 58 | */ 59 | public function getName() 60 | { 61 | return 'database_connections'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/AppBundle/Connection/Wrapper.php: -------------------------------------------------------------------------------- 1 | _params = $params; 33 | parent::__construct($params, $driver, $config, $eventManager); 34 | } 35 | /** 36 | * @param $host 37 | * @param $dbname 38 | * @param $username 39 | * @param $password 40 | */ 41 | public function forceSwitch($host, $dbname, $username, $password, $connect = true) 42 | { 43 | if ($this->isConnected()) { 44 | $this->close(); 45 | } 46 | $this->_params['host'] = $host; 47 | $this->_params['dbname'] = $dbname; 48 | $this->_params['user'] = $username; 49 | $this->_params['password'] = $password; 50 | 51 | if($connect) { 52 | $this->connect(); 53 | } 54 | } 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | public function connect() 59 | { 60 | if ($this->isConnected()) { 61 | return true; 62 | } 63 | 64 | $this->_conn = $this->_driver->connect( 65 | $this->_params, 66 | $this->_params['user'], 67 | $this->_params['password'], 68 | $this->_params['driverOptions'] 69 | ); 70 | 71 | if ($this->_eventManager->hasListeners(Events::postConnect)) { 72 | $eventArgs = new ConnectionEventArgs($this); 73 | $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); 74 | } 75 | 76 | $this->isConnected = true; 77 | 78 | return true; 79 | } 80 | 81 | /** 82 | * {@inheritDoc} 83 | */ 84 | public function isConnected() 85 | { 86 | return $this->isConnected; 87 | } 88 | 89 | /** 90 | * {@inheritDoc} 91 | */ 92 | public function close() 93 | { 94 | if ($this->isConnected()) { 95 | parent::close(); 96 | $this->isConnected = false; 97 | } 98 | } 99 | 100 | /** 101 | * @return array 102 | */ 103 | public function getParams() 104 | { 105 | return $this->_params; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/AppBundle/Controller/DefaultController.php: -------------------------------------------------------------------------------- 1 | get('doctrine.dbal.tenant_connection')->isConnected()) 20 | { 21 | $invoices = $this->get('app_bundle.repository.invoice')->findAll(); 22 | $name = $this->get('doctrine.dbal.tenant_connection')->getDatabase(); 23 | } 24 | 25 | return $this->render('default/index.html.twig', [ 26 | 'tenants' => $this->get('app_bundle.repository.tenant')->findAll(), 27 | 'name' => $name, 28 | 'invoices' => $invoices 29 | ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/AppBundle/DataFixtures/Standard/LoadTenantsData.php: -------------------------------------------------------------------------------- 1 | 'superman', 'server' => 'localhost', 'database' => 'multidb_superman', 'username' => 'root', 'password' => 'root'], 14 | ['id' => 'spiderman', 'server' => 'localhost', 'database' => 'multidb_spiderman', 'username' => 'root', 'password' => 'root'], 15 | ['id' => 'batman', 'server' => 'localhost', 'database' => 'multidb_batman', 'username' => 'root', 'password' => 'root'] 16 | ]; 17 | 18 | foreach($data as $tenant) { 19 | $object = Tenant::fromArray($tenant); 20 | $manager->persist($object); 21 | } 22 | 23 | $manager->flush(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AppBundle/DataFixtures/Tenant/LoadInvoicesData.php: -------------------------------------------------------------------------------- 1 | "01/01/2017", 'company' => 'ABC', 'createdAt' => new DateTime()], 16 | ["number" => "10/01/2017", 'company' => 'CDE', 'createdAt' => new DateTime('yesterday')], 17 | ["number" => "20/01/2017", 'company' => 'EFG', 'createdAt' => new DateTime('+1 month')], 18 | ]; 19 | 20 | foreach($data as $tenant) { 21 | $object = Invoice::fromArray($tenant); 22 | $manager->persist($object); 23 | } 24 | 25 | $manager->flush(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/AppBundle/Entity/Tenant.php: -------------------------------------------------------------------------------- 1 | id = $id; 31 | $this->dbname = $database; 32 | $this->username = $username; 33 | $this->password = $password; 34 | $this->server = $server; 35 | } 36 | 37 | public static function fromArray($array) 38 | { 39 | return new self($array['id'], $array['database'], $array['username'], $array['password'], $array['server']); 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getId() 46 | { 47 | return $this->id; 48 | } 49 | 50 | /** 51 | * @return string 52 | */ 53 | public function getDatabase() 54 | { 55 | return $this->dbname; 56 | } 57 | 58 | /** 59 | * @return string 60 | */ 61 | public function getUsername() 62 | { 63 | return $this->username; 64 | } 65 | 66 | /** 67 | * @return string 68 | */ 69 | public function getPassword() 70 | { 71 | return $this->password; 72 | } 73 | 74 | /** 75 | * @return string 76 | */ 77 | public function getServer() 78 | { 79 | return $this->server; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/AppBundle/Entity/TenantRepository.php: -------------------------------------------------------------------------------- 1 | find($tenant); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/AppBundle/Listener/CommandListener.php: -------------------------------------------------------------------------------- 1 | tenantProvider = $tenantProvider; 33 | $this->connectionWrapper = $connectionWrapper; 34 | $this->schemaManager = $schemaManager; 35 | $this->allowedCommands = $allowedCommands; 36 | } 37 | 38 | /** 39 | * @param ConsoleCommandEvent $event 40 | * @throws \Exception 41 | */ 42 | public function onConsoleCommand(ConsoleCommandEvent $event) 43 | { 44 | $command = $event->getCommand(); 45 | $input = $event->getInput(); 46 | 47 | if (!$this->isProperCommand($command)) { 48 | return; 49 | } 50 | 51 | $command->getDefinition()->addOption( 52 | new InputOption('tenant', null, InputOption::VALUE_OPTIONAL, 'tenant name', null) 53 | ); 54 | 55 | if(!$command->getDefinition()->hasOption('em')) { 56 | $command->getDefinition()->addOption( 57 | new InputOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') 58 | ); 59 | } 60 | 61 | $input->bind($command->getDefinition()); 62 | 63 | if(is_null($input->getOption('tenant'))) { 64 | $event->getOutput()->write('default: '); 65 | return; 66 | } 67 | 68 | $tenantName = $input->getOption('tenant'); 69 | 70 | $input->setOption('em', 'tenant'); 71 | $command->getDefinition()->getOption('em')->setDefault('tenant'); 72 | 73 | /** @var Tenant $tenant */ 74 | $tenant = $this->tenantProvider->getTenant($tenantName); 75 | 76 | if($tenant === null) { 77 | throw new Exception(sprintf('Tenant identified as %s does not exists', $tenantName)); 78 | } 79 | 80 | $this->connectionWrapper->forceSwitch( 81 | $tenant->getServer(), $tenant->getDatabase(), $tenant->getUsername(), $tenant->getPassword(), false 82 | ); 83 | 84 | $event->getOutput()->writeln( 85 | sprintf('%s@%s: ', $tenant->getUsername(), $tenant->getDatabase()) 86 | ); 87 | } 88 | 89 | /** 90 | * @param Command $command 91 | * @return bool 92 | */ 93 | private function isProperCommand(Command $command) 94 | { 95 | return in_array($command->getName(), $this->allowedCommands); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/AppBundle/Listener/DatabaseListener.php: -------------------------------------------------------------------------------- 1 | getCommand(); 30 | $input = $event->getInput(); 31 | 32 | if (!$this->isProperCommand($command)) { 33 | return; 34 | } 35 | 36 | if($input->hasOption('tenant') && $input->getOption('tenant') !== null) { 37 | $connection = 'tenant'; 38 | } else { 39 | $connection = 'default'; 40 | } 41 | 42 | $input->setOption('connection', $connection); 43 | $input->setOption('em', $connection); 44 | $command->getDefinition()->getOption('connection')->setDefault($connection); 45 | $command->getDefinition()->getOption('em')->setDefault($connection); 46 | } 47 | 48 | /** 49 | * @param Command $command 50 | * @return bool 51 | */ 52 | private function isProperCommand(Command $command) 53 | { 54 | return in_array($command->getName(), $this->allowedCommands); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/AppBundle/Listener/FixturesListener.php: -------------------------------------------------------------------------------- 1 | options = $options; 31 | } 32 | 33 | /** 34 | * @param ConsoleCommandEvent $event 35 | * @throws \Exception 36 | */ 37 | public function onConsoleCommand(ConsoleCommandEvent $event) 38 | { 39 | $command = $event->getCommand(); 40 | $input = $event->getInput(); 41 | 42 | if (!$this->isProperCommand($command)) { 43 | return; 44 | } 45 | 46 | if($input->hasOption('tenant') && $input->getOption('tenant') !== null) { 47 | $dir = $this->options['tenant']; 48 | } else { 49 | $dir = $this->options['default']; 50 | } 51 | 52 | $command->getDefinition()->getOption('fixtures')->setDefault([$dir]); 53 | } 54 | 55 | /** 56 | * @param Command $command 57 | * @return bool 58 | */ 59 | private function isProperCommand(Command $command) 60 | { 61 | return in_array($command->getName(), $this->allowedCommands); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/AppBundle/Listener/TenantListener.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 24 | $this->tenantProvider = $tenantProvider; 25 | } 26 | 27 | public function onKernelRequest(GetResponseEvent $event) 28 | { 29 | if (!$event->isMasterRequest()) { 30 | return; 31 | } 32 | 33 | $query = []; 34 | parse_str($event->getRequest()->getQueryString(), $query); 35 | 36 | if(array_key_exists('db', $query)) { 37 | $tenant = $this->tenantProvider->getTenant($query['db']); 38 | $this->connection->forceSwitch($tenant->getServer(), $tenant->getDatabase(), $tenant->getUsername(), $tenant->getPassword()); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/config/doctrine/App/Tenant.orm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AppBundle\Entity\Tenant: 3 | type: entity 4 | table: tenants 5 | repositoryClass: AppBundle\Entity\TenantRepository 6 | 7 | id: 8 | id: 9 | type: string 10 | 11 | fields: 12 | server: 13 | type: string 14 | nullable: false 15 | length: 255 16 | dbname: 17 | type: string 18 | nullable: false 19 | length: 255 20 | name: db 21 | username: 22 | type: string 23 | nullable: false 24 | length: 255 25 | password: 26 | type: string 27 | nullable: false 28 | length: 255 29 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/config/doctrine/Client/Invoice.orm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | Client\Invoice: 3 | type: entity 4 | table: invoices 5 | repositoryClass: Client\InvoiceRepository 6 | 7 | id: 8 | number: 9 | type: guid 10 | generator: { strategy: UUID } 11 | 12 | fields: 13 | company: 14 | type: string 15 | nullable: false 16 | length: 255 17 | createdAt: 18 | type: datetime 19 | nullable: false 20 | length: 255 21 | -------------------------------------------------------------------------------- /src/AppBundle/Resources/views/Toolbar/DatabaseConnections.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'WebProfilerBundle:Profiler:layout.html.twig' %} 2 | 3 | {% block toolbar %} 4 | 23 | 24 |
    25 |
  1. {{ collector.getDefault() }}
  2. 26 |
  3. {{ collector.getTenant() }}
  4. 27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /src/AppBundle/TenantProviderInterface.php: -------------------------------------------------------------------------------- 1 | request('GET', '/'); 14 | 15 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); 16 | $this->assertContains('Welcome to Symfony', $crawler->filter('#container h1')->text()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Client/Invoice.php: -------------------------------------------------------------------------------- 1 | number = $number; 26 | $this->company = $company; 27 | $this->createdAt = $createdAt; 28 | } 29 | 30 | /** 31 | * @param $array 32 | * @return Invoice 33 | */ 34 | public static function fromArray($array) 35 | { 36 | return new self($array['number'], $array['company'], $array['createdAt']); 37 | } 38 | 39 | /** 40 | * @return string 41 | */ 42 | public function getNumber() 43 | { 44 | return $this->number; 45 | } 46 | 47 | /** 48 | * @return string 49 | */ 50 | public function getCompany() 51 | { 52 | return $this->company; 53 | } 54 | 55 | /** 56 | * @return DateTime 57 | */ 58 | public function getCreatedAt() 59 | { 60 | return $this->createdAt; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Client/InvoiceRepository.php: -------------------------------------------------------------------------------- 1 | 17 | Options -MultiViews 18 | 19 | 20 | 21 | RewriteEngine On 22 | 23 | # Determine the RewriteBase automatically and set it as environment variable. 24 | # If you are using Apache aliases to do mass virtual hosting or installed the 25 | # project in a subdirectory, the base path will be prepended to allow proper 26 | # resolution of the app.php file and to redirect to the correct URI. It will 27 | # work in environments without path prefix as well, providing a safe, one-size 28 | # fits all solution. But as you do not need it in this case, you can comment 29 | # the following 2 lines to eliminate the overhead. 30 | RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$ 31 | RewriteRule ^(.*) - [E=BASE:%1] 32 | 33 | # Sets the HTTP_AUTHORIZATION header removed by apache 34 | RewriteCond %{HTTP:Authorization} . 35 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 36 | 37 | # Redirect to URI without front controller to prevent duplicate content 38 | # (with and without `/app.php`). Only do this redirect on the initial 39 | # rewrite by Apache and not on subsequent cycles. Otherwise we would get an 40 | # endless redirect loop (request -> rewrite to front controller -> 41 | # redirect -> request -> ...). 42 | # So in case you get a "too many redirects" error or you always get redirected 43 | # to the start page because your Apache does not expose the REDIRECT_STATUS 44 | # environment variable, you have 2 choices: 45 | # - disable this feature by commenting the following 2 lines or 46 | # - use Apache >= 2.3.9 and replace all L flags by END flags and remove the 47 | # following RewriteCond (best solution) 48 | RewriteCond %{ENV:REDIRECT_STATUS} ^$ 49 | RewriteRule ^app\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L] 50 | 51 | # If the requested filename exists, simply serve it. 52 | # We only want to let Apache serve files and not directories. 53 | RewriteCond %{REQUEST_FILENAME} -f 54 | RewriteRule .? - [L] 55 | 56 | # Rewrite all other queries to the front controller. 57 | RewriteRule .? %{ENV:BASE}/app.php [L] 58 | 59 | 60 | 61 | 62 | # When mod_rewrite is not available, we instruct a temporary redirect of 63 | # the start page to the front controller explicitly so that the website 64 | # and the generated links can still be used. 65 | RedirectMatch 302 ^/$ /app.php/ 66 | # RedirectTemp cannot be used instead 67 | 68 | 69 | -------------------------------------------------------------------------------- /web/app.php: -------------------------------------------------------------------------------- 1 | loadClassCache(); 11 | //$kernel = new AppCache($kernel); 12 | 13 | // When using the HttpCache, you need to call the method in your front controller instead of relying on the configuration parameter 14 | //Request::enableHttpMethodParameterOverride(); 15 | $request = Request::createFromGlobals(); 16 | $response = $kernel->handle($request); 17 | $response->send(); 18 | $kernel->terminate($request, $response); 19 | -------------------------------------------------------------------------------- /web/app_dev.php: -------------------------------------------------------------------------------- 1 | loadClassCache(); 27 | $request = Request::createFromGlobals(); 28 | $response = $kernel->handle($request); 29 | $response->send(); 30 | $kernel->terminate($request, $response); 31 | -------------------------------------------------------------------------------- /web/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uirapuru/multidb/0953d48083cb3374633a982d8ba6ae6a928c0c3f/web/apple-touch-icon.png -------------------------------------------------------------------------------- /web/config.php: -------------------------------------------------------------------------------- 1 | getFailedRequirements(); 30 | $minorProblems = $symfonyRequirements->getFailedRecommendations(); 31 | $hasMajorProblems = (bool) count($majorProblems); 32 | $hasMinorProblems = (bool) count($minorProblems); 33 | 34 | ?> 35 | 36 | 37 | 38 | 39 | 40 | Symfony Configuration Checker 41 | 331 | 332 | 333 |
334 |
335 | 338 | 339 | 359 |
360 | 361 |
362 |
363 |
364 |

Configuration Checker

365 |

366 | This script analyzes your system to check whether is 367 | ready to run Symfony applications. 368 |

369 | 370 | 371 |

Major problems

372 |

Major problems have been detected and must be fixed before continuing:

373 |
    374 | 375 |
  1. getTestMessage() ?> 376 |

    getHelpHtml() ?>

    377 |
  2. 378 | 379 |
380 | 381 | 382 | 383 |

Recommendations

384 |

385 | Additionally, toTo enhance your Symfony experience, 386 | it’s recommended that you fix the following: 387 |

388 |
    389 | 390 |
  1. getTestMessage() ?> 391 |

    getHelpHtml() ?>

    392 |
  2. 393 | 394 |
395 | 396 | 397 | hasPhpIniConfigIssue()): ?> 398 |

* 399 | getPhpIniConfigPath()): ?> 400 | Changes to the php.ini file must be done in "getPhpIniConfigPath() ?>". 401 | 402 | To change settings, create a "php.ini". 403 | 404 |

405 | 406 | 407 | 408 |

All checks passed successfully. Your system is ready to run Symfony applications.

409 | 410 | 411 | 416 |
417 |
418 |
419 |
Symfony Standard Edition
420 |
421 | 422 | 423 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uirapuru/multidb/0953d48083cb3374633a982d8ba6ae6a928c0c3f/web/favicon.ico -------------------------------------------------------------------------------- /web/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 3 | 4 | User-agent: * 5 | Disallow: 6 | --------------------------------------------------------------------------------