Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Composer

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Composer: Because using external Libraries should be Fun!

Welcome to the brand new world of PHP with Composer!

One of the great things about PHP is that we’re huge! But how big are we really? PHP consists of a lot of successful, but extremely isolated libraries, like Wordpress, Drupal, Joomla and all of the PHP frameworks like Symfony and Zend. Historically, these different groups share almost nothing, which means that in reality, we’re all pretty small islands. That’s right, even though we’re the biggest group in the world, we’ve somehow turned ourselves into the underdogs.

Well, it’s time for a whole new beautiful era where your community is all of the PHP world.

PHP developers haven’t typically shared code or used outside libraries because, well, it sucked! To use just one outside library, you’d need to tackle at least three major issues:

1) First, how do I autoload the PHP classes in the library? Autoloading is the background machine that makes it possible to reference PHP classes without using require or include statements. When I bring in outside code, I either need to figure out which files to include or how to configure that library’s autoloader.

2) Second, we need to know if this library depends on any other libraries. And if it does, that’s yet another library I need to download and configure.

3) Finally, how should I store the library in my project? Should I use SVN externals? Git submodules? Just commit the whole darn thing into my project?

The answer to these 3 problems is Composer. Want to do something crazy like bring Symfony’s Finder component into a Drupal project? We’re about to learn just how easy that is. But wait! Before you non-Drupal people run off, the process and code you’ll see have nothing to do with Drupal. This is one of the first great things about using Composer: the process for using external libraries is the same now in any PHP project.

Our Project

Move into the directory where your project lives. Our first goal is to use a standalone Symfony component called “Finder” to list all the files in a directory and print them onto the page. Just to make things really exciting, we’re going to do this inside a custom Drupal module.

To start, I’ve created a function called get_current_files deep inside my project, which returns an array of filenames:

// sites/all/modules/list_files/list_files.module
// ...

function get_current_files()
{
    return array('foo');
}

Tip

I’ve already created a module called list_files and enabled it before starting the tutorial.

I’ve also gone far enough to print these onto the screen. Ok great, now let’s get to work!

Installation

The first thing you’ll need to do is download the Composer executable. Go to GetComposer.org, click “Download”, then copy one of the two code blocks depending if you have curl installed.

$ curl -s https://getcomposer.org/installer | php

Composer itself is just an executable file, and this fancy bit of code downloads the file and makes sure your system is setup to use Composer. If you see any errors or warnings during this step, you may need to tweak your PHP configuration.

If everything went ok, you’ll have a brand new composer.phar sitting at the root of your project. Ok, let’s put this guy to work! Execute the composer script by typing php composer.phar:

$ php composer.phar

This shows you a list of all the available composer commands: we’ll get to know some of these in the next few minutes.

Creating the composer.json File

So far, we have a composer.phar, but that’s it! Composer’s main job is to download third-party libraries into a vendor/ directory in your project. To tell Composer which libraries you need, your project needs to have a composer.json configuration file. Instead of creating this file by hand, let’s use the first Composer command: init.

$ php composer.phar init

The init command will ask you several questions about your project, but unless you’re planning to open-source it, don’t worry too much about your answers. Finally, it’ll ask you to interactively define your dependencies. By “dependencies”, Composer is asking you which third-party libraries you want to include in your project. To start, we’re going to add a Symfony2 component called Finder - and you can find its documentation at symfony.com.

At the prompt, simply type “finder” and wait for the results. Behind the scenes, Composer is searching against a giant central repository of packages called “Packagist”, which you can search directly at packagist.org. In the language of Composer, a package is just an individual directory that you want to download into your project. A typical package contains PHP classes, but it can really contain anything.

Each package has a unique name, and your first job is to find the name of the one you need. Ideally, the name is included in the documentation for the library, but even if its not, you can often find it just by searching. On Packagist, searching for “finder” reveals a package called symfony/finder, which is definitely the right one!

Back at the terminal, our search for “finder” has returned a bunch of results including many versions of symfony/finder. The second thing we need to figure out is which version we want. The safest choice is to choose the latest stable release, which should follow the X.X.X format. In our case, this is v2.1.2. If a version ends in -dev, it’s a development branch, which may be stable or unstable based on the library. The dev-master version is special, and it always means the latest, bleeding-edge code. If you use a lesser-known package, dev-master may be your only option.

Let’s choose to install v2.1.2. When it asks you to install “dev dependencies” interactively, choose no. You probably won’t need to worry about dev dependencies, but if you’re curious about them, check out Composer’s documentation. Finally, confirm generation and add vendor/ to your .gitignore file if you’re storing your project with git:

# .gitignore
/vendor/

The end-product of the init command is the new composer.json file that’s now in your project. Open it and check out the require key: this is really the only important part of this file right now and it simply tells Composer which packages your project needs. We could have created this file by hand - the init task is just there for convenience.

{
    "name": "weaverryan/drupal",
    "require": {
        "symfony/finder": "v2.1.2"
    },
    "authors": [
        {
            "name": "Ryan Weaver",
            "email": "ryan@thatsquality.com"
        }
    ]
}

Using the “install” command

At this point, we’ve downloaded the Composer executable and created the composer.json config file. To actually put Composer to work, run php composer.phar install. This is the most important command: it reads the composer.json file and downloads all the needed libraries into the vendor directory:

$ php composer.phar install

And look, a vendor directory!

vendor/
    composer/
    symfony/
        finder/
            Symfony/...

It contains a symfony directory that holds the Finder library and a few other things that help with autoloading - which is one of the most powerful features of Composer.

Autoloading

Now that Composer has downloaded the Finder library, let’s use it! To keep things simple, I’ll paste in some Finder code that looks for all gif files that have been modified within the past day:

// sites/all/modules/list_files/list_files.module
// ...

/**
 * A utility function to return the array of current SplFileInfo objects
 */
function get_current_files()
{
    // the "files" directory
    $dir = drupal_realpath(file_default_scheme() . '://');

    $finder = new \Symfony\Component\Finder\Finder();
    $finder->in($dir)
        ->name('*.gif')
        ->date('since 1 day ago')
    ;
    $files = array();
    foreach ($finder as $file) {
        $files[] = $file->getFilename();
    }

    return $files;
}

This code should work, but when we refresh the page, we get a class not found error!

Class SymfonyComponentFinderFinder not found.

Of course! Even though Composer downloaded the Finder library for us, we can’t use any of its PHP classes without including them.

Fortunately, Composer solves this for us - through autoloading. The exact details of how autoloading works goes beyond this screencast, but the important thing is that Composer helps us out. To use Composer’s autoloader, simply include the vendor/autoload.php file somewhere in your project. For now, let’s put it right inside this function:

// sites/all/modules/list_files/list_files.module
// ...

function get_current_files()
{
    require __DIR__.'/../../../../vendor/autoload.php';

    // ...
}

Refresh the page again. It works! By including Composer’s autoloader, the Finder library - as well as the PHP classes for any other libraries we included via Composer - are made available to us automatically.

To make our third-party classes available anywhere, it would be even better to include the autoload file in some central, bootstrap file in your project. For Drupal, this might be the settings.php file:

// sites/default/settings.php
require __DIR__.'/../../vendor/autoload.php';

// ... the rest of the file

When we refresh, everything still works.

The composer.lock file and “install” versus “update”

Things are going so well that I think we should add another library! So let’s get crazy! Head back to packagist.org and find a library symfony/filesystem. To tell Composer that we want this package, just edit the composer.json by hand, add a second entry under the require with the name of the library. To make things more interesting, let’s use the 2.1.x-dev version, which will give us the latest commit on the 2.1 branch:

{
    "name": "weaverryan/drupal",
    "require": {
        "symfony/finder": "v2.1.2",
        "symfony/filesystem": "2.1.x-dev"
    },
    "authors": [
        {
            "name": "Ryan Weaver",
            "email": "ryan@thatsquality.com"
        }
    ]
}

Next, we need to tell Composer to re-read this file and download the new library.

Before, we used the install command to do this. But if you try that command now, it prints out a few lines, but doesn’t actually do anything. Why not?

# does not download the new library :/
$ php composer.phar install

When it comes to downloading the libraries we need, composer actually has two different commands: install and update.

When we ran the install command earlier, one of the things it did was create a composer.lock file that recorded the exact versions of all libraries that it downloaded at that exact moment.

Normally, the install command actually ignores the composer.json file and reads all of the information from this lock file instead. If you make a change to composer.json and run php composer.phar install, that change won’t be used. The lock file is important, because if multiple developers are using a project, each one can run php composer.phar install and receive identical versions of all libraries, even if new commits have been added to them.

In fact, the only time that the install command reads the composer.json file is when you first start the project, because the lock file doesn’t exist yet.

In this one case, install acts exactly like the update command, which always ignores the lock file and reads the composer.json file instead. This checks and potentially upgrades all the libraries in composer.json and updates composer.lock when it finishes.

What this ultimately means is that you should use a simple workflow. Unless you’re adding a new library or intentionally upgrading something, always use composer.phar install.

Using the update Command

When you do need to add a new library or upgrade something. Use composer.phar update. You can be even more precise by calling update and passing it the name of the library you’re updating. By doing this, Composer will only update that library, instead of all of them.

$ php composer.phar update symfony/filesystem

Icing in the Cake: The require Command

Also, Composer has a cool shortcut command for adding new libraries into your project:

$ php composer.phar require

Tip

You can use the require command to search for a library. In this example, I searched for the doctrine/dbal package and added it.

With the require command, you can search for the package you need and Composer will automatically update your composer.json for you and run the update command to download the library. In this case, when I included the doctrine/dbal package, an extra packaged called doctrine/common was downloaded. This is dependency management in action. Composer is smart enough to know that doctrine/dbal depends on doctrine/common and it downloads it for you. Woo!

Version Control

The lock file is especially important if you have multiple developers so that you can be sure that each person has identical vendor libraries. To make this possible, commit both your composer.json file and your composer.lock file:

$ git add composer.json
$ git add composer.lock
$ git add .gitignore
$ git add sites

Typically composer.phar is ignored, since each developer can download it individually.

Now, let’s pretend like we’re a new developer that’s pulling down the codebase.

$ cd ..
$ git clone drupal drupal2
$ cd drupal2

Notice that the project doesn’t have a vendor/ directory yet, because we didn’t commit the vendor files. In fact, we ignored the vendor directory in git, because Composer can populate it for us.

I’ll copy in the composer.phar file from the previous directory and then run php composer.phar install.

$ cp ../drupal/composer.phar .
$ php composer.phar install

This reads the composer.lock file and downloads everything we need into the vendor/ directory. And just like that, your new developer has a functional project!

Conclusion

There’s a lot more that Composer can do, but you already understand how to find libraries, manage your composer.json file, use Composer’s autoloader and download the external libraries with the update or install commands.

If you’d like to learn more, check out the documentation at GetComposer.org. One interesting topic is scripts - which are callbacks that are executed before or after packages are installed. Other important topics include the dump-autoload command, “dev dependencies”, minimum stability, and installing Composer globally. If you want to start your Symfony project using Composer checkout our latest version of Getting Started in Symfony. Good luck and See ya next time!

Leave a comment!

8
Login or Register to join the conversation
Nayte91 Avatar
Nayte91 Avatar Nayte91 | posted 3 years ago

Hello Ryan & team, and thank you for all your work, I'm absolutely loving all your tutorials ! By far the best courses I found on the internet, as I'm beginning my programmers journey, I can relate on this.

This composer tutorial made me think : Isn't an idea to display somewhere (subtitle, under the video, on the course list, ...) some context informations about your courses ?

_Date of recording
_Last edition (Updated for sf4.2 etc)
_The versionning of every component used in this course (PHPStorm: 2018.2, Composer: 1.8.2, Symfony: 3.3, PHP: 7.1, etc.. Even push further with symfony components version), and this course is validated for (PHPStorm: actual, Composer: actual, Symfony: until 5.0, PHP: until 7.4, etc..) <-- this was totally non-real exemples, don't search any logic !

I Know some of the informations are recorded (I can't even imagine you don't have a createdAt column in your db :) ), some of them are displayed (you put headband on some courses updated or deprecated), but I think beeing more consistant and rigorous with "versionning" can have huge benefits :
_For users who come and measure how usable can be a tutorial, and follow it with the insurance that it's actual, in a single glance,

_For you to track precisely what you have to update/revamp, according to each tool used in a video.

I'm actually EATING the courses of your site, so if you're interested I can keep a track of which version of what is in every courses, that can be a starting point ?

Anyway, thank you for reading me and thinking a bit about it, I really think it's one of the rare defaults here ! Road to perfection !

Regards,

Reply

Hi Julien R.!

Welcome - and thank you VERY much for your kind words. It means a lot, and I'll pass them to the whole team ❤️

About your idea, we think alike ;). We currently have a feature that's in its *final* stages of development that will do almost all of this: it will tell you the version of the major piece of technology being taught (e.g. "Symfony 4"), but will also expose the specific version of all of the dependencies - e.g. "symfony/framework-bundle" 4.2.3. In addition, you're 100% correct that even this doesn't tell the whole story. Even if a tutorial is built on Symfony 4.2, we might be keeping it up-to-date and valid for all versions of Symfony. So, when we need to, we also add a descriptive note about that - e.g. "This tutorial was built in Symfony 4.2, but is valid for all versions of Symfony 4". All of this will be deployed... hopefully as early as next week?

Here's a preview of what it would look like, for a particularly old tutorial: https://imgur.com/undefined

How does that work for you? We aren't planning to show a recorded data because we think that could cause a "false positive" "this is too old" feeling. Most technology will change fairly quickly and invalidate tutorials over a few years. But some are solid and can last much longer without needing to be updated. The version of Composer and PhpStorm *shouldn't* affect things in a significant way (except for possibly the phpstorm and composer tutorials [this one!] where we could/should indeed list them - I hadn't thought about those 2 cases :)).

Let me know - I really appreciate the feedback and conversation!

Cheers!

1 Reply
Nayte91 Avatar
Nayte91 Avatar Nayte91 | weaverryan | posted 3 years ago | edited

Hello weaverryan,

Thank you for taking time to answer me !

The imgur link doesn't work for some reasons, but I'll just seek and enjoy the new version as soon as it will be live.

Your question about the age of a tutorial deserves a real discussion.

you're 100% right, I can relate on : dates hit me more than "versions". When I see a 3 years old, 4 years old content, I'm not willing to click.. And I'm sure my 2016's me would hate me for that !

Maybe I behave like this because I don't want to see something "out of date" ? I already followed a bootstrap 3 tutorial with practical works, on bootstrap 4 (I didn't know about the difference) : NEVER AGAIN..
Or, I am a hipster and I always want to see something about shiny new technology ? I don't know. For example, knowing that "the gang of 4" classified the actual design patterns 25 bloody years ago doesn't compute in my head. I accept this for science, but not for IT; Hipster thing surely.

You're absolutely right about the date displaying, and that's why the 2nd part ("from sf3.0" "to sf5.1") of my precedent suggestion is soooo important : it's not just about keeping records on your side for some reasons; It's about the contract with the user. The guarantee. The API.

False positive :
_"This is too old" but the tutorial is still up to date ! The user lost an opportunity to learn something and go away... --> With this information, he knows he can follow your tutorial: you guarantee it.

False negative :
_"This is OK" but the tutorial is deprecated ! The user tries to fit circles into squares with deprecated components, get frustrated and lose faith... --> With this information, he knows he must skip these cool-but-deprecated videos: you warned him.

The bonus hidden case :
_I'm a generic dev, my company works only on a sf3.4 app and it's not going to change, so I'm searching for good tutorials. But when I see brand new shining one, maybe this is not for me. Aaaaand you have a false positive on "it's too new".

Because in fact you absolutely can do a tutorial on sf5.1 now that is backward compatible with sf4.1 for example !

So ultimately, you don't really say:

"this tutorial was recorded on symfony 2.2 but it stills OK for symfony 5.1".


But instead:

"this tutorial is compatible for symfony versions between 2.2 and 5.1".

This way, you don't really have to give the information "created date" and "last revision date" as it's not relevant anymore (unless you write "revision date" as "certification date", to reassure people about how active you are). "From" & "to" are OK to 99% of use cases, with a simple color code for validated, partly deprecated or fully deprecated courses.

We are lucky, we currently have a real case scenario : Authentication !
Sf5.1 proposes a new authentication workflow, and will be (let's say it will be) the only one standing on sf6.0.
You actually have an awesome tutorial about "Beautiful Authentication, Powerful Authorization". This one is sf4' Spacebar related, but maybe it is ok with sf3.3 (I don't really know).
So this "Beautiful Authentication, Powerful Authorization" will have information "compatible from sf3.3 to sf5.4".
Then, imagine you make a new tutorial about authentication, using the brand new system. You're late, and you publish it when sf6.1 is out.
So this "new authentication tutorial" will have information "compatible from sf5.1 to sf6.1".
And at every new version of sf out, like sf6.2, you check if the authentication stills the same (2 seconds for you), validate & update your course, "compatible from sf5.1 to sf6.2".

Of course, you can improve the granularity by storing this information for each symfony component or PHP tools, with your new system. You can even imagine a "filter section" on the left of your courses list, like on e-shopping sites you know : version of forms, version of httpfoundation, brand, color, price... Wait, is there a make:filter command ??

I really think this is the best organization you have to either ensure every user finds what he searches for, and you to track efficiently your numerous courses.

Please take a few minutes to think about it, appropriate the idea, and let's see how it grows !

Thank you very much again all your work,
I'm going to make some suggestion to the make:reset-password bundle I played with :p

Reply

Hey Julien R.!

Thanks for the long reply - I read all of it, and really appreciate it!

> It's about the contract with the user. The guarantee. The API.

💯💯💯

That's the idea that resonates with me as well. I want to have an agreement with our users that a specific tutorial is valid for a specific "range" of Symfony versions (or whatever the main tech is).

> Then, imagine you make a new tutorial about authentication, using the brand new system. You're late, and you publish it when sf6.1 is out.
> So this "new authentication tutorial" will have information "compatible from sf5.1 to sf6.1".

So this is very interesting. As I mentioned, we *do* have (in the upcoming feature) a place for exactly this message as well as a simple way just to "tag" a tutorial with a "main" technology version that it uses - e.g. "Symfony 5". Here's the proper Imgur link - https://imgur.com/a/6OvOJv1 - you can see the "Symfony 2" in the background but more information when you enquire.

But what I hadn't really thought about was a specific range... especially one that could span to another Symfony major version - e.g. Sf 5.1 to 6.1 (while waiting for the Symfony 6 security tutorial). That's really interesting. I'll take this feedback to the team. The trick with all of this is maintaining these versions in some "sane" way - we have MANY tutorials, and checking and updating them by hand every 6 months would be a lot of work, not to mention error-prone. That being said, especially with Symfony's versioning and deprecation policy, there are some ways that I think this could be automated.

Cheers!

1 Reply
Default user avatar
Default user avatar Arturas Lapinskas | posted 5 years ago

having problems with the fixtures, when i run php app/console doctrine:fixtures:load i got You have requested a non-existent parameter "database_path" im using sqlite database, i found the solution for composer havig same problem here http://stackoverflow.com/qu... but how to use it in the doctrine:fixtures?

Reply

Hey Arturas!

This typically just means that you're missing a database_path parameter in your app/config/parameters.yml file in Symfony. Actually, if you're getting this error when running the fixtures command, you should be getting this error when you do anything - i.e. load a page - because Symfony can't boot until all of the parameters have been defined. In other words, because you have a %database_path% in your app/config/config.yml file, you need to have a database_path parameter defined in app/config/parameters.yml.

Let me know if that helps :)

Reply
Default user avatar
Default user avatar Arturas Lapinskas | weaverryan | posted 5 years ago

Yes, that helps, thanks a lot!

Reply
Default user avatar
Default user avatar Dustin Garza | posted 5 years ago

very nice! I love the narration...it added to the simplicity as it has a good flow! Thanks again

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

userVoice