potherca / katwizy
🌟 A cleaner Symfony install for light-weight projects. 🏁🚗💨
Requires
- composer/composer: ^1.2
- sensio/generator-bundle: ^3.0
- symfony/framework-standard-edition: ^3.0
- vlucas/phpdotenv: ^2.4
Requires (Dev)
- phpunit/phpunit: ^5.6
README
🔔 Introduction
🌟 Helping your project make a clean finish. 🏁🚗💨
🎯 Project Goals
Katwizy hat the following goals:
- Project code should be the only code in a project repository
- All vendor code should live in the
vendor
directory1 - All files that need to be in a project directory should be generated and
.gitignore
'd - A projects composer file should be clean and as free from framework entries as possible
- Katwizy should only be a thin layer between a project and Symfony.
- Functionality should be configurable
- Configuration should not be done in code
1 This includes all Symfony code as well.
🏗 Installation
Install the Katwizy package through composer:
composer require potherca/katwizy
This will also install the Symfony framework2.
2 And all of the bundles that come with the standard Symfony install.
🌟 Usage
Katwizy hides all of the standard Symfony files out of sight, so your project only has to implement the parts that it needs.
Minimal example
The absolute minimum that is required to get started is a web
folder that
contains an index.php
file that does the following:
- Get the Composer Autoloader
- Declare a Controller (including a Route)
- Run the bootstrap
Such a file could look like this:
// web/index.php
<?php
$loader = require dirname(__DIR__).'/vendor/autoload.php';
class Website extends Symfony\Bundle\FrameworkBundle\Controller\Controller
{
/**
* @Sensio\Bundle\FrameworkExtraBundle\Configuration\Route("/")
* @Sensio\Bundle\FrameworkExtraBundle\Configuration\Route("/{name}")
*/
final public function homepage($name = 'world')
{
return new Symfony\Component\HttpFoundation\Response(
sprintf('<p>Hello %s!</p>', $name)
);
}
}
Potherca\Katwizy\Bootstrap::run(
$loader,
Symfony\Component\HttpFoundation\Request::createFromGlobals()
);
/*EOF*/
There is a separate repository with this example
Next steps
There are several things that could be done next:
- Declare the Controller in a separate file. All files in the
src
directory are also scanned for Route annotations - Declare routes in a separate config file, rather than using annotations.
Configuration
There are various things that can be configured by adding certain files to the
config
directory:
Bundles
Bundles can be added to the AppKernel from a bundles.yml
file. It is not
needed (or possible) to edit the AppKernel. Separate sections must be defined
for prod
, test
and dev
bundles. All bundles defined in the prod
section
will also be loaded in test
and dev
environments.
General Configuration
General configuration can be managed from a separate config.yml
file.
Parameters
Parameters that are to be used in other configuration files can be stored in a
parameters.yml
file. This file will be loaded before the other configuration
files.
Emulating Environmental Variables
Besides the parameters file, it is also possible to use an so called .env
file.
This is done by adding a file named .env
to a projects root directory. Entries
in the .env
file will be loaded for use from getenv()
/$_ENV
/$_SERVER
.
For variables in the .env
file need to be loaded in the Symfony configuration,
they need to be prefixed with SYMFONY__
, as described in the Symfony cookbook.
To have a period "." in the config name, use a double underscore "__" in the
variable name. (Double underscores will be replaced by a period, as a period is
not a valid character in an environment variable name). Variables can also be
nested by wrapping an existing environment variable in ${…}
For instance:
BASE_DIR="/var/webroot/project-root"
CACHE_DIR="${BASE_DIR}/cache"
TMP_DIR="${BASE_DIR}/tmp"
Routes
Routes can be configured from a routes.yml
file.
Security
Security can be configured from a security.yml
file. A security configuration
from the Symfony Standard Edition is already loaded by default.
Services
Services can be configured from a services.yml
file.
Composer Script Commands
It is not uncommon for the scripts
section in a composer.json
file to grow
quite large. A simple solution is to create a script that holds all of the
entries that should be called.
Katwizy provides an abstract base class AbstractScriptEventHandler
that makes
it trivial to add commands to be called from the Composer Scripts.
All that an extending classes has to do is implement the getCommands
function
and add the command class to the script
section in the project's composer.json
file.
Extending the AbstractScriptEventHandler
An implementation might look something like this:
<?php
use Composer\Script\ScriptEvents;
use Composer\Script\Event;
use Potherca\Katwizy\Command\ImmutableCommand;
use Potherca\Katwizy\Command\ScriptEventHandler;
class ScriptEventHandler extends ScriptEventHandler
{
/**
* Return commands to be run after composer install/update.
*
* To only call certain scripts on specific events, get the event name (with
* `$event->getName()`) and check it against the available events in
* Composer\Script\ScriptEvents. The most commonly used events are:
*
* - ScriptEvents::POST_INSTALL_CMD
* - ScriptEvents::POST_UPDATE_CMD
*
* Command provided by the Symfony `console` command should be marked as
* `COMMAND_TYPE_SYMFONY`. These will be handled the same as COMMAND_TYPE_SHELL
*
* @param Event $event
*
* @return ImmutableCommand[]
*
* @throws \InvalidArgumentException
*/
final public function getCommands(Event $event)
{
$commands = [];
if (in_array($event->getName(), [ScriptEvents::POST_UPDATE_CMD, ScriptEvents::POST_INSTALL_CMD])) {
$commands[] = new ImmutableCommand(
ImmutableCommand::COMMAND_TYPE_SYMFONY,
'assets:install'
);
}
if ($event->getName() === ScriptEvents::POST_UPDATE_CMD) {
$commands[] = new ImmutableCommand(
ImmutableCommand::COMMAND_TYPE_PHP,
'Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache'
);
}
return $commands;
}
}
/*EOF*/
Adding script to composer.json
After the Commad class has been created it can be added to the composer file like this:
{
"require": {
"…": "…"
}
"scripts": {
"symfony-scripts": ["\\ScriptEventHandler::handleEvent"],
"post-install-cmd": ["@symfony-scripts"],
"post-update-cmd": ["@symfony-scripts"]
}
}
More details on configuration
As routes.yml
, security.yml
and services.yml
are loaded separately, there
is no need to include
them from the general config file.
For config.yml
, parameters.yml
, routes.yml
, security.yml
and services.yml
files, Katwizy will first look for a environment specific file before looking for
a generic file. For instance, for a config file Katwizy will first look for a
config_prod.yml
file (or config_test.yml
or config_dev.yml
, depending on
the environment). If that is not found it will look for a config.yml
file.
This allows for creating a generic configuration that can be included from environment specific files. Any environment that does not have a specific congif file will default to the generic config file.
Available commands
- The Symfony
console
command is available from the vendorbin
directory This isvendor/bin/
by default (but this can be configured fromcomposer.json
) - The
flotsam
command (also in the vendorbin
directory) can be run to symlink other functionality from the Symfony Standard Edition into the project directory (and add them to the.gitignore
file).
Debugging
By default, Symfony contains two separate files for web access: app.php
for
production and app_dev.php
for development. When accessing the first, debugging
mode is disabled, when accessing the latter, debugging mode is enabled. Besides
being the cause of various inconveniences, this causes a problem if/when a
developer would like to debug things in production.
Although there is an environment variable SYMFONY_DEBUG
, this is only honored
by Symfony's configuration files, not the app(_dev).php
files.
With Katwizy, the environment variable SYMFONY_DEBUG
is honoured by the
index.php
.3
Besides this, debugging mode can be trigger by setting an environment variable
named DEBUG_TOKEN
and referencing this from a requests GET or POST debug-token
variable, from a Cookie's debug-token
value or from a DEBUG-TOKEN
header.
For ease-of-use, when the debugging token is set with a GET, POST or Header, it will automatically be set as a Cookie value.
3 It is also honored by the console
command.
🤖 How it works
Katwizy implements a custom Kernel which changes where Symfony looks for things.
More information about how this works can be found in the Symfony manual pages about the micro kernel trait, overriding Symfony directory structure and Symfony Kernel Configuration.
📝 Other information
©️ License
The Source Code for this project is available under a GPL-3.0+ License (GNU General Public License v3.0 or higher) – Created by Potherca
💡 Origin / Motivation
The Symfony manual offers various ways to get started with a new project.
They all (more or less) lead to a script that dumps a bunch4 of files in a directory of your choice.
The manual then tells you to just go ahead and commit all that mess into git. For small projects and proof-of-concepts this is basically a lot of vendor code poluting your clean new code base.
A large part of these files will never be edited, leaving your repository strewn with files that have "Initial commit" as message. Forever.
As a final insult, the created composer.json
file is full of verbose entries
that could be a lot shorter if other means were used5.
There must be a better way.
Katwizy tries to offer this "better way".
4 To be precise: 38 files in 18 folders.
5 Like a composer meta-package, a single file to add composer-scripts
to, a Symfony composer plugin, a config file to link to from the extra
section.
🤔 About the name
Katwizy offers lightweight transportation.The name is a portmanteau of two light-weight car models. The Ford Ka and the Renault Twizy.
It is not a Polish translation of "Cat Visa". That was just a happy coincidence.
💮 About the logo
❓FAQ
❔What is wrong with Symfony code in your project?
The way that Symfony is structured (like most frameworks these days) is in such a way that the directories don't actually show anything about the domain the application is involved with. There is a keynote by Uncle Bob from the Ruby Midwest 2011 conference that explains the problem this poses in quite some details.
❔Why not simply use Silex?
The most commonly suggested solution to have a "lightweight Symfony" is to use Silex. There is, however, one problems with that... Silex is not Symfony. This means that yet another framework needs to be learned, Bundles will not work6 and the problem shifts from one framework-oriented directory structure to another framework's directory structure. So... no thanks.
6 And no, not all functionality from bundles are available in Silex through other means.
❔ Which packages/bundles are installed?
The following bundles and packages are installed with Katwizy:
Packages
- vlucas/phpdotenv - Loads environment variables from
.env
togetenv()
/$_ENV
/$_SERVER
automagically.
Bundles
- DoctrineBundle - Adds support for the Doctrine ORM
- FrameworkBundle - The core Symfony framework bundle
- MonologBundle - Adds support for Monolog, a logging library
- SecurityBundle - Adds security by integrating Symfony's security component
- SensioFrameworkExtraBundle - Adds several enhancements, including template and routing annotation capability
- SwiftmailerBundle - Adds support for Swiftmailer, a library for sending emails
- TwigBundle - Adds support for the Twig templating engine
Only in in dev
/test
environment
- DebugBundle - Adds Debug and VarDumper component integration
- SensioDistributionBundle - Adds functionality for configuring and working with Symfony distributions
- SensioGeneratorBundle - Adds code generation capabilities
- WebProfilerBundle - Adds profiling functionality and the web debug toolbar
❔Whats with all of the emoji's and footnotes?
I don't like my documentation boring and colourless. That's all.