AJ Technologies

Developing for the (green) Web

Running Symfony2 on the Php 5.4 Internal Webserver and Assets 404

| Comments

TL;DR

Use the app/console server:run method to start a correct symfony2 server site instead of just using php -S

Long version

I’m working on a docker container for a client, so the working environment is almost the same as the production environment and doesn’t interfere with other projects that use other dependencies. More on Docker in a later post.

Working on this Docker container I tried to get Symfony2 running with the easiest option, so not configuring apache-modphp or nginx-phpfpm, but just using the internal webserver that php offers since 5.4. As this is just a development envirement, this should be fine.

I started with this command, which seemed to work:

/root/of/your/project
1
php -S 0.0.0.0:8000 -t web web/app_dev.php

However, when visiting the site in question on http://0.0.0.0:8000 all assets error out with a 404.

Not sure what was happening, I debugged it and tried it on other sites where it worked fine.

For Symfony 2 there is a console command you can run to get the webserver running, which adds a small router file to let the internal webserver know where certain files are.

/root/of/your/project
1
php app/console server:run  -vvv 0.0.0.0:8000

The -vvv option defines the verbosity, normally you can skip it, but as I wanted to see what was happening I added it. Visiting my site now at http://0.0.0.0:8000 gave a normal functioning site as I expected.

So what does the server:run command do?

The ps fax command shows what is happening when the server:run command is run:

/root/of/your/project
1
2
3
4
#ps fax
 9 ?        S+     0:00 php app/console server:run -vvv 0.0.0.0:8005
29 ?        S+     0:00  \_ sh -c '/usr/bin/php5' '-S' '0.0.0.0:8005' '/server/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php'
30 ?        S+     0:05      \_ /usr/bin/php5 -S 0.0.0.0:8005 /server/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php

The server:run command starts a php webserver with a special router_dev.php file, that looks like this:

/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php
1
2
3
4
5
6
7
if (is_file($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.$_SERVER['SCRIPT_NAME'])) {
    return false;
}

$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'app_dev.php';

require 'app_dev.php';

So basically what happens is that a check is done on the filesystem to see if the file exists. If so, a false is returned to the webserver so it knows that it needs to deliver it directly to the browser instead of forwarding the request to the app_dev.php file into our symfony2 application.

Hope this helps someone avoid a couple of hours searching for why it doesn’t work.

Next post will be a dockerfile for the symfony2 standard edition so you can easily code away on a new project without needing some hours to set it up with docker.

Switching From Bower to Composer With Components

| Comments

Last time I talked about Deploying a symfony2 app with capifony and bower.

Working with this setup for a few months I found a few problems:

  • Deploys get a lot slower
  • Strange errors with cache clear and cache warmup
  • Why not use composer as we already use it for other dependencies

Looking for a better solution I found out most of my dependencies were already on packagist, so I could just include them in my composer.json

/composer.json
1
2
3
4
5
{
  "require": {
      "components/jquery": "1.10.*"
  }
}

Now while running composer.phar install you’ll get the following output:

php composer.phar install
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing symfony/process (v2.3.6)
    Loading from cache

  - Installing kriswallsmith/assetic (v1.1.2)
    Loading from cache

  - Installing robloach/component-installer (0.0.12)
    Loading from cache

  - Installing components/jquery (1.10.2)
    Loading from cache

kriswallsmith/assetic suggests installing twig/twig (Assetic provides the integration with the Twig templating engine)
kriswallsmith/assetic suggests installing leafo/lessphp (Assetic provides the integration with the lessphp LESS compiler)
kriswallsmith/assetic suggests installing leafo/scssphp (Assetic provides the integration with the scssphp SCSS compiler)
kriswallsmith/assetic suggests installing ptachoire/cssembed (Assetic provides the integration with phpcssembed to embed data uris)
kriswallsmith/assetic suggests installing leafo/scssphp-compass (Assetic provides the integration with the SCSS compass plugin)
Writing lock file
Generating autoload files
Compiling component files

Composer uses the robloach/component-installer to install the components in /vendor/components along with assetic and symfony process which it uses to generate a require.js file you can directly use in your project. This require.js (and require.css) can be found in /components/require.js

If you’re using Symfony2 and want to use assetic instead of the require.js file, you can configure that in your config.yml like so:

/app/config.yml
1
2
3
4
5
6
7
# Assetic Configuration
assetic:
    ...
    assets:
        jquery_js:
                    inputs:
                        - %kernel.root_dir%/../web/components/jquery/jquery.js

Usage is then just using this asset in your template files:

/app/Resources/views/base.html.twig
1
2
3
4
5
6
{% javascripts
            "@jquery_js"
            output='compiled/main.js'
%}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}

Ok, so what packages are available on packagist?

Just fill in components on packagist which already has 44 pages. Ok, so not all of those are real components, there are some other php packages as well, but most popular libs are already available this way.

I need component X and it’s not on packagist, what to do?

Just add your own repository to composer.json and figure out which specific files you need in your project. The following is an example with jqplot

/composer.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
    "repositories": [
        {
            "type": "package",
            "package": {
                "name": "components/jqplot",
                "type": "component",
                "version": "1.0.8",
                "dist": {
                    "url": "https://bitbucket.org/cleonello/jqplot/downloads/jquery.jqplot.1.0.8r1250.zip",
                    "type": "zip"
                },
                "extra": {
                    "component": {
                        "scripts": [
                            "excanvas.js",
                            "jquery.jqplot.js",
                            "plugins/*.js"
                        ],
                        "styles": [
                            "jquery.jqplot.css"
                        ]
                    }
                },
                "require": {
                    "robloach/component-installer": "*"
                }
            }
        }
    ],
    "require": {
      "components/jqplot": "1.0.8",
  }
}
  • L6: Add a name for the package, which you use later on in the require
  • L9 Add a repository with a dist to a zipfile (jqplot is on bitbucket but works as well for github or normal http urls)
  • l7 specify type: ‘component’
  • L25 require the robloach/component-installer so it is handled by the component installer by composer
  • L32 require your package with the name you’ve given in L6 and the version in L8

  • Lines 13 till 24 are important as they define which files will be copied to the /components/jqplot directory from the /vendords/components/jqplot directory. In this case we’ll need excanvas, the jquery.jqplot.js file and all plugins. And we include the jquery.jqplot.css style as well.

Check out the documentation on robloach/component-installer for more information.

Conclusion

As most js and css packages are on packagist anyway and composer is really good integrated into capifony, this solution works a lot better than depending on bower. Adding a package not on packagist is a bit more work, but not that difficult. Try it out yourself!

Deploying a Symfony2 App With Capifony and Bower

| Comments

UPDATE: See Switching from bower to composer with components for a better solution.


Today I wanted to deploy a new Symfony2 app I’m working on.

In this app I’m using the SPBowerBundle to use the bower package manager for all the 3rd party frontend code, like Twitter Bootstrap, Jquery and Jqplot.

Capifony normally works great for deploying symfony2 apps (if you’re not using it, you should check it out!), but this time it didn’t.

The setup of the SPBowerBundle is very straightforward, but if you want to deploy it with capifony you need to setup a few things that aren’t really obvious.

1) As Capifony by default does not run the scripts included by Composer, step 6 of the SPBowerBundle of the installation manual doesn’t work. Instead you need to add the following to your deploy.rb:

/app/config/deploy.rb
1
2
3
4
5
6
7
8
9
10
11
12
before 'symfony:cache:warmup', 'symfony:sp:bower:install'

namespace :symfony do
  namespace :sp do
      namespace :bower do
      desc '[internal] Run the bower install'
      task :install do
          invoke_command "cd #{latest_release} && app/console sp:bower:install --env=prod", :via => run_method
      end
    end
  end
end

Before the cache is warmed up, the SPBowerBundle installs the specified assets, so they are available for the specified assetic filters.

2) Make sure you setup the cache warmup of the SPBowerBundle, step 7

app/config/config.yml
1
2
3
# app/config/config.yml
sp_bower:
    install_on_warmup: true


as otherwise you get the following error:

1
2
 [Sp\BowerBundle\Bower\Exception\MappingException]
Bower returned an invalid dependency mapping. This mostly happens when the dependencies are not yet installed or if you are using an old version of bower.

If you did that, and you still get the above mentioned exception, copy your components.json to your server and try a bower install on it. If that works, it’s a configuration issue in capifony, if not, then you should check your bower install.