7. Coding Practices
7.1 The Basics
PHP is a vast language that allows coders of all levels the ability to produce code not only quickly, but efficiently. However, while advancing through the language, we often forget the basics that we first learnt (or overlooked) in favor of short cuts and/or bad habits. To help combat this common issue, this section is aimed at reminding coders of the basic coding practices within PHP.
- Continue reading on The Basics
7.2 Design Patterns
When you are building your application it is helpful to use common patterns in your code and common patterns for the overall structure of your project. Using common patterns is helpful because it makes it much easier to manage your code and lets other developers quickly understand how everything fits together.
If you use a framework then most of the higher level code and project structure will be based on that framework, so a lot of the pattern decisions are made for you. But it is still up to you to pick out the best patterns to follow in the code you build on top of the framework. If, on the other hand, you are not using a framework to build your application then you have to find the patterns that best suit the type and size of application that you’re building.
You can learn more about PHP design patterns and see working examples at:
- https://refactoring.guru/design-patterns/php
- https://designpatternsphp.readthedocs.io/ (PDF download)
7.3 Internationalization (i18n) and Localization (l10n)
Disclaimer for newcomers: i18n and l10n are numeronyms, a kind of abbreviation where numbers are used to shorten words - in our case, internationalization becomes i18n and localization, l10n.
First of all, we need to define those two similar concepts and other related things:
- Internationalization is when you organize your code so it can be adapted to different languages or regions without refactorings. This action is usually done once - preferably, at the beginning of the project, or else you will probably need some huge changes in the source!
- Localization happens when you adapt the interface (mainly) by translating contents, based on the i18n work done before. It usually is done every time a new language or region needs support and is updated when new interface pieces are added, as they need to be available in all supported languages.
- Pluralization defines the rules required between distinct languages to interoperate strings containing numbers and counters. For instance, in English when you have only one item, it is singular, and anything different from that is called plural; plural in this language is indicated by adding an S after some words, and sometimes changes parts of it. In other languages, such as Russian or Serbian, there are two plural forms in addition to the singular - you may even find languages with a total of four, five or six forms, such as Slovenian, Irish or Arabic.
7.4 Common ways to implement
The easiest way to internationalize PHP software is by using array files and using those strings in templates, such as
<h1><?=$TRANS['title_about_page']?></h1>. This way is, however, hardly recommended for serious projects, as it poses
some maintenance issues along the road - some might appear in the very beginning, such as pluralization. So, please,
don’t try this if your project will contain more than a couple of pages.
The most classic way and often taken as reference for i18n and l10n is a Unix tool called gettext. It dates
back to 1995 and is still a complete implementation for translating software. It is easy enough to get running, while
still sporting powerful supporting tools. It is about Gettext we will be talking here. Also, to help you not get messy
over the command-line, we will be presenting a great GUI application that can be used to easily update your l10n source.
Other tools
There are common libraries used that support Gettext and other implementations of i18n. Some of them may seem easier to install or sport additional features or i18n file formats. In this document, we focus on the tools provided with the PHP core, but here we list others for completion:
-
aura/intl: Provides internationalization (I18N) tools, specifically package-oriented per-locale message
translation. It uses array formats for messages. Does not provide a message extractor, but does provide advanced
message formatting via the
intlextension (including pluralized messages). -
php-gettext/Gettext: Gettext support with an OO interface; includes improved helper functions, powerful
extractors for several file formats (some of them not supported natively by the
gettextcommand), and can also export to other formats besides.mo/.pofiles. Can be useful if you need to integrate your translation files into other parts of the system, like a JavaScript interface. -
symfony/translation: supports a lot of different formats, but recommends using verbose XLIFF’s. Doesn’t
include helper functions nor a built-in extractor, but supports placeholders using
strtr()internally. - laminas/laminas-i18n: supports array and INI files, or Gettext formats. Implements a caching layer to save you from reading the filesystem every time. It also includes view helpers, and locale-aware input filters and validators. However, it has no message extractor.
Other frameworks also include i18n modules, but those are not available outside of their codebases:
-
Laravel supports basic array files, has no automatic extractor but includes a
@langhelper for template files. -
Yii supports array, Gettext, and database-based translation, and includes a messages extractor. It is backed by the
Intlextension, available since PHP 5.3, and based on the ICU project; this enables Yii to run powerful replacements, like spelling out numbers, formatting dates, times, intervals, currency, and ordinals.
If you decide to go for one of the libraries that provide no extractors, you may want to use the gettext formats, so you can use the original gettext toolchain (including Poedit) as described in the rest of the chapter.
7.5 Gettext
Installation
You might need to install Gettext and the related PHP library by using your package manager, like apt-get or yum.
After installed, enable it by adding extension=gettext.so (Linux/Unix) or extension=php_gettext.dll (Windows) to
your php.ini.
Here we will also be using Poedit to create translation files. You will probably find it in your system’s package manager; it is available for Unix, macOS, and Windows, and can be downloaded for free on their website as well.
Structure
Types of files
There are three files you usually deal with while working with gettext. The main ones are PO (Portable Object) and MO (Machine Object) files, the first being a list of readable “translated objects” and the second, the corresponding binary to be interpreted by gettext when doing localization. There’s also a POT (Template) file, which simply contains all existing keys from your source files, and can be used as a guide to generate and update all PO files. Those template files are not mandatory: depending on the tool you are using to do l10n, you can go just fine with only PO/MO files. You will always have one pair of PO/MO files per language and region, but only one POT per domain.
Domains
There are some cases, in big projects, where you might need to separate translations when the same words convey different meaning given a context. In those cases, you split them into different domains. They are, basically, named groups of POT/PO/MO files, where the filename is the said translation domain. Small and medium-sized projects usually, for simplicity, use only one domain; its name is arbitrary, but we will be using “main” for our code samples. In Symfony projects, for example, domains are used to separate the translation for validation messages.
Locale code
A locale is simply a code that identifies one version of a language. It is defined following the ISO 639-1 and ISO 3166-1 alpha-2 specs: two lower-case letters for the language, optionally followed by an underline and two upper-case letters identifying the country or regional code. For rare languages, three letters are used.
For some speakers, the country part may seem redundant. In fact, some languages have dialects in different
countries, such as Austrian German (de_AT) or Brazilian Portuguese (pt_BR). The second part is used to distinguish
between those dialects - when it is not present, it is taken as a “generic” or “hybrid” version of the language.
Directory structure
To use Gettext, we will need to adhere to a specific structure of folders. First, you will need to select an arbitrary
root for your l10n files in your source repository. Inside it, you will have a folder for each needed locale, and a
fixed LC_MESSAGES folder that will contain all your PO/MO pairs. Example:
1 <project root>
2 ├─ src/
3 ├─ templates/
4 └─ locales/
5 ├─ forum.pot
6 ├─ site.pot
7 ├─ de/
8 │ └─ LC_MESSAGES/
9 │ ├─ forum.mo
10 │ ├─ forum.po
11 │ ├─ site.mo
12 │ └─ site.po
13 ├─ es_ES/
14 │ └─ LC_MESSAGES/
15 │ └─ ...
16 ├─ fr/
17 │ └─ ...
18 ├─ pt_BR/
19 │ └─ ...
20 └─ pt_PT/
21 └─ ...
Plural forms
As we said in the introduction, different languages might sport different plural rules. However, gettext saves us from
this trouble once again. When creating a new .po file, you will have to declare the plural rules for that
language, and translated pieces that are plural-sensitive will have a different form for each of those rules. When
calling Gettext in code, you will have to specify the number related to the sentence, and it will work out the correct
form to use - even using string substitution if needed.
Plural rules include the number of plurals available and a boolean test with n that would define in which rule the
given number falls (starting the count with 0). For example:
- Japanese:
nplurals=1; plural=0- only one rule - English:
nplurals=2; plural=(n != 1);- two rules, first if N is one, second rule otherwise - Brazilian Portuguese:
nplurals=2; plural=(n > 1);- two rules, second if N is bigger than one, first otherwise
Now that you understood the basis of how plural rules works - and if you didn’t, please look at a deeper explanation on the LingoHub tutorial -, you might want to copy the ones you need from a list instead of writing them by hand.
When calling out Gettext to do localization on sentences with counters, you will have to provide it the
related number as well. Gettext will work out what rule should be in effect and use the correct localized version.
You will need to include in the .po file a different sentence for each plural rule defined.
Sample implementation
After all that theory, let’s get a little practical. Here’s an excerpt of a .po file - don’t mind with its format,
but with the overall content instead; you will learn how to edit it easily later:
1 msgid ""
2 msgstr ""
3 "Language: pt_BR\n"
4 "Content-Type: text/plain; charset=UTF-8\n"
5 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
6
7 msgid "We are now translating some strings"
8 msgstr "Nós estamos traduzindo algumas strings agora"
9
10 msgid "Hello %1$s! Your last visit was on %2$s"
11 msgstr "Olá %1$s! Sua última visita foi em %2$s"
12
13 msgid "Only one unread message"
14 msgid_plural "%d unread messages"
15 msgstr[0] "Só uma mensagem não lida"
16 msgstr[1] "%d mensagens não lidas"
The first section works like a header, having the msgid and msgstr especially empty. It describes the file encoding,
plural forms and other things that are less relevant.
The second section translates a simple string from English to
Brazilian Portuguese, and the third does the same, but leveraging string replacement from sprintf so the
translation may contain the user name and visit date.
The last section is a sample of pluralization forms, displaying
the singular and plural version as msgid in English and their corresponding translations as msgstr 0 and 1
(following the number given by the plural rule). There, string replacement is used as well so the number can be seen
directly in the sentence, by using %d. The plural forms always have two msgid (singular and plural), so it is
advised not to use a complex language as the source of translation.
Discussion on l10n keys
As you might have noticed, we are using as source ID the actual sentence in English. That msgid is the same used
throughout all your .po files, meaning other languages will have the same format and the same msgid fields but
translated msgstr lines.
Talking about translation keys, there are two main “schools” here:
-
msgidas a real sentence. The main advantages are:- if there are pieces of the software untranslated in any given language, the key displayed will still maintain some meaning. Example: if you happen to translate by heart from English to Spanish but need help to translate to French, you might publish the new page with missing French sentences, and parts of the website would be displayed in English instead;
- it is much easier for the translator to understand what’s going on and do a proper translation based on the
msgid; - it gives you “free” l10n for one language - the source one;
- The only disadvantage: if you need to change the actual text, you would need to replace the same
msgidacross several language files.
-
msgidas a unique, structured key. It would describe the sentence role in the application in a structured way, including the template or part where the string is located instead of its content.- it is a great way to have the code organized, separating the text content from the template logic.
- however, that could bring problems to the translator that would miss the context. A source language file would be
needed as a basis for other translations. Example: the developer would ideally have an
en.pofile, that translators would read to understand what to write infr.pofor instance. - missing translations would display meaningless keys on screen (
top_menu.welcomeinstead ofHello there, User!on the said untranslated French page). That is good it as would force translation to be complete before publishing - however, bad as translation issues would be remarkably awful in the interface. Some libraries, though, include an option to specify a given language as “fallback”, having a similar behavior as the other approach.
The Gettext manual favors the first approach as, in general, it is easier for translators and users in case of trouble. That is how we will be working here as well. However, the Symfony documentation favors keyword-based translation, to allow for independent changes of all translations without affecting templates as well.
Everyday usage
In a typical application, you would use some Gettext functions while writing static text in your pages. Those sentences
would then appear in .po files, get translated, compiled into .mo files and then, used by Gettext when rendering
the actual interface. Given that, let’s tie together what we have discussed so far in a step-by-step example:
1. A sample template file, including some different gettext calls
1 <?php include 'i18n_setup.php' ?>
2 <div id="header">
3 <h1><?=sprintf(gettext('Welcome, %s!'), $name)?></h1>
4 <!-- code indented this way only for legibility -->
5 <?php if ($unread): ?>
6 <h2><?=sprintf(
7 ngettext('Only one unread message',
8 '%d unread messages',
9 $unread),
10 $unread)?>
11 </h2>
12 <?php endif ?>
13 </div>
14
15 <h1><?=gettext('Introduction')?></h1>
16 <p><?=gettext('We\'re now translating some strings')?></p>
-
gettext()simply translates amsgidinto its correspondingmsgstrfor a given language. There’s also the shorthand function_()that works the same way; -
ngettext()does the same but with plural rules; - There are also
dgettext()anddngettext(), that allow you to override the domain for a single call. More on domain configuration in the next example.
2. A sample setup file (i18n_setup.php as used above), selecting the correct locale and configuring Gettext
1 <?php
2 /**
3 * Verifies if the given $locale is supported in the project
4 * @param string $locale
5 * @return bool
6 */
7 function valid($locale) {
8 return in_array($locale, ['en_US', 'en', 'pt_BR', 'pt', 'es_ES', 'es']);
9 }
10
11 //setting the source/default locale, for informational purposes
12 $lang = 'en_US';
13
14 if (isset($_GET['lang']) && valid($_GET['lang'])) {
15 // the locale can be changed through the query-string
16 $lang = $_GET['lang']; //you should sanitize this!
17 setcookie('lang', $lang); //it's stored in a cookie so it can be reused
18 } elseif (isset($_COOKIE['lang']) && valid($_COOKIE['lang'])) {
19 // if the cookie is present instead, let's just keep it
20 $lang = $_COOKIE['lang']; //you should sanitize this!
21 } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
22 // default: look for the languages the browser says the user accepts
23 $langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
24 array_walk($langs, function (&$lang) { $lang = strtr(strtok($lang, ';'), ['-' => '_']); });
25 foreach ($langs as $browser_lang) {
26 if (valid($browser_lang)) {
27 $lang = $browser_lang;
28 break;
29 }
30 }
31 }
32
33 // here we define the global system locale given the found language
34 putenv("LANG=$lang");
35
36 // this might be useful for date functions (LC_TIME) or money formatting (LC_MONETARY), for in\
37 stance
38 setlocale(LC_ALL, $lang);
39
40 // this will make Gettext look for ../locales/<lang>/LC_MESSAGES/main.mo
41 bindtextdomain('main', '../locales');
42
43 // indicates in what encoding the file should be read
44 bind_textdomain_codeset('main', 'UTF-8');
45
46 // if your application has additional domains, as cited before, you should bind them here as w\
47 ell
48 bindtextdomain('forum', '../locales');
49 bind_textdomain_codeset('forum', 'UTF-8');
50
51 // here we indicate the default domain the gettext() calls will respond to
52 textdomain('main');
53
54 // this would look for the string in forum.mo instead of main.mo
55 // echo dgettext('forum', 'Welcome back!');
56 ?>
3. Preparing translation for the first run
One of the great advantages Gettext has over custom framework i18n packages is its extensive and powerful file format. “Oh man, that’s quite hard to understand and edit by hand, a simple array would be easier!” Make no mistake, applications like Poedit are here to help - a lot. You can get the program from their website, it’s free and available for all platforms. It’s a pretty easy tool to get used to, and a very powerful one at the same time - using all features Gettext has available. This guide is based on PoEdit 1.8.
In the first run, you should select “File > New…” from the menu. You’ll be asked straight ahead for the language:
here you can select/filter the language you want to translate to, or use that format we mentioned before, such as
en_US or pt_BR.
Now, save the file - using that directory structure we mentioned as well. Then you should click “Extract from sources”, and here you’ll configure various settings for the extraction and translation tasks. You’ll be able to find all those later through “Catalog > Properties”:
- Source paths: here you must include all folders from the project where
gettext()(and siblings) are called - this is usually your templates/views folder(s). This is the only mandatory setting; - Translation properties:
- Project name and version, Team and Team’s email address: useful information that goes in the .po file header;
- Plural forms: here go those rules we mentioned before - there’s a link in there with samples as well. You can leave it with the default option most of the time, as PoEdit already includes a handy database of plural rules for many languages.
- Charsets: UTF-8, preferably;
- Source code charset: set here the charset used by your codebase - probably UTF-8 as well, right?
- Source keywords: The underlying software knows how
gettext()and similar function calls look like in several programming languages, but you might as well create your own translation functions. It will be here you’ll add those other methods. This will be discussed later in the “Tips” section.
After setting those points it will run a scan through your source files to find all the localization calls. After every scan PoEdit will display a summary of what was found and what was removed from the source files. New entries will fed empty into the translation table, and you’ll start typing in the localized versions of those strings. Save it and a .mo file will be (re)compiled into the same folder and ta-dah: your project is internationalized.
4. Translating strings
As you may have noticed before, there are two main types of localized strings: simple ones and those with plural forms. The first ones have simply two boxes: source and localized string. The source string cannot be modified as Gettext/Poedit do not include the powers to alter your source files - you should change the source itself and rescan the files. Tip: you may right-click a translation line and it will hint you with the source files and lines where that string is being used. On the other hand, plural form strings include two boxes to show the two source strings, and tabs so you can configure the different final forms.
Whenever you change your sources and need to update the translations, just hit Refresh and Poedit will rescan the code, removing non-existent entries, merging the ones that changed and adding new ones. It may also try to guess some translations, based on other ones you did. Those guesses and the changed entries will receive a “Fuzzy” marker, indicating it needs review, appearing golden in the list. It is also useful if you have a translation team and someone tries to write something they are not sure about: just mark Fuzzy, and someone else will review later.
Finally, it is advised to leave “View > Untranslated entries first” marked, as it will help you a lot to not forget any entry. From that menu, you can also open parts of the UI that allow you to leave contextual information for translators if needed.
Tips & Tricks
Possible caching issues
If you are running PHP as a module on Apache (mod_php), you might face issues with the .mo file being cached. It
happens the first time it is read, and then, to update it, you might need to restart the server. On Nginx and PHP5 it
usually takes only a couple of page refreshes to refresh the translation cache, and on PHP7 it is rarely needed.
Additional helper functions
As preferred by many people, it is easier to use _() instead of gettext(). Many custom i18n libraries from
frameworks use something similar to t() as well, to make translated code shorter. However, that is the only function
that sports a shortcut. You might want to add in your project some others, such as __() or _n() for ngettext(),
or maybe a fancy _r() that would join gettext() and sprintf() calls. Other libraries, such as
php-gettext’s Gettext also provide helper functions like these.
In those cases, you’ll need to instruct the Gettext utility on how to extract the strings from those new functions.
Don’t be afraid; it is very easy. It is just a field in the .po file, or a Settings screen on Poedit. In the editor,
that option is inside “Catalog > Properties > Source keywords”. Remember: Gettext already knows the default functions
for many languages, so don’t be afraid if that list seems empty. You need to include there the specifications of those
new functions, following a specific format:
- if you create something like
t()that simply returns the translation for a string, you can specify it ast. Gettext will know the only function argument is the string to be translated; - if the function has more than one argument, you can specify in which one the first string is - and if needed, the
plural form as well. For instance, if we call our function like this:
__('one user', '%d users', $number), the specification would be__:1,2, meaning the first form is the first argument, and the second form is the second argument. If your number comes as the first argument instead, the spec would be__:2,3, indicating the first form is the second argument, and so on.
After including those new rules in the .po file, a new scan will bring in your new strings just as easy as before.
References
- Wikipedia: i18n and l10n
- Wikipedia: Gettext
- LingoHub: PHP internationalization with gettext tutorial
- PHP Manual: Gettext
- Gettext Manual
7.6 Date and Time
PHP has a class named DateTime to help you when reading, writing, comparing or calculating with date and time. There are many date and time related functions in PHP besides DateTime, but it provides nice object-oriented interface to most common uses. DateTime can handle time zones, but that is outside the scope of this short introduction.
To start working with DateTime, convert raw date and time string to an object with createFromFormat() factory method
or do new DateTime to get the current date and time. Use format() method to convert DateTime back to a string for
output.
1 <?php
2 $raw = '22. 11. 1968';
3 $start = DateTime::createFromFormat('d. m. Y', $raw);
4
5 echo 'Start date: ' . $start->format('Y-m-d') . PHP_EOL;
Calculating with DateTime is possible with the DateInterval class. DateTime has methods like add() and sub() that
take a DateInterval as an argument. Do not write code that expects the same number of seconds in every day. Both daylight
saving and time zone alterations will break that assumption. Use date intervals instead. To calculate date difference
use the diff() method. It will return new DateInterval, which is super easy to display.
1 <?php
2 // create a copy of $start and add one month and 6 days
3 $end = clone $start;
4 $end->add(new DateInterval('P1M6D'));
5
6 $diff = $end->diff($start);
7 echo 'Difference: ' . $diff->format('%m month, %d days (total: %a days)') . PHP_EOL;
8 // Difference: 1 month, 6 days (total: 37 days)
You can use standard comparisons on DateTime objects:
1 <?php
2 if ($start < $end) {
3 echo "Start is before the end!" . PHP_EOL;}
One last example to demonstrate the DatePeriod class. It is used to iterate over recurring events. It can take two DateTime objects, start and end, and the interval for which it will return all events in between.
1 <?php
2 // output all thursdays between $start and $end
3 $periodInterval = DateInterval::createFromDateString('first thursday');
4 $periodIterator = new DatePeriod($start, $periodInterval, $end, DatePeriod::EXCLUDE_START_DATE\
5 );
6 foreach ($periodIterator as $date) {
7 // output each date in the period
8 echo $date->format('Y-m-d') . ' ';
9 }
A popular PHP API extension is Carbon. It inherits everything in the DateTime class, so involves minimal code alterations, but extra features include Localization support, further ways to add, subtract and format a DateTime object, plus a means to test your code by simulating a date and time of your choosing.
- Read about DateTime
- Read about date formatting (accepted date format string options)
7.7 PEAR
A veteran package manager that some PHP developers enjoy is PEAR. It behaves similarly to Composer, but has some notable differences.
PEAR requires each package to have a specific structure, which means that the author of the package must prepare it for usage with PEAR. Using a project which was not prepared to work with PEAR is not possible.
PEAR installs packages globally, which means after installing them once they are available to all projects on that server. This can be good if many projects rely on the same package with the same version but might lead to problems if version conflicts between two projects arise.
How to install PEAR
You can install PEAR by downloading the .phar installer and executing it. The PEAR documentation has
detailed install instructions for every operating system.
If you are using Linux, you can also have a look at your distribution package manager. Debian and Ubuntu,
for example, have an apt php-pear package.
How to install a package
If the package is listed on the PEAR packages list, you can install it by specifying the official name:
1 pear install foo
If the package is hosted on another channel, you need to discover the channel first and also specify it when
installing. See the Using channel docs for more information on this topic.
Handling PEAR dependencies with Composer
If you are already using Composer and you would like to install some PEAR code too, you can use Composer to handle your PEAR dependencies. PEAR repositories are no longer directly supported by Composer version 2, so you must manually add a repository to install PEAR packages:
1 {
2 "repositories": [
3 {
4 "type": "package",
5 "package": {
6 "name": "pear2/pear2-http-request",
7 "version": "2.5.1",
8 "dist": {
9 "url": "https://github.com/pear2/HTTP_Request/archive/refs/heads/master.zi\
10 p",
11 "type": "zip"
12 }
13 }
14 }
15 ],
16 "require": {
17 "pear2/pear2-http-request": "*"
18 },
19 "autoload": {
20 "psr-4": {"PEAR2\\HTTP\\": "vendor/pear2/pear2-http-request/src/HTTP/"}
21 }
22 }
The first section "repositories" will be used to let Composer know it should “initialize” (or “discover” in PEAR
terminology) the pear repo. Then the require section will prefix the package name like this:
pear-channel/package
The “pear” prefix is hardcoded to avoid any conflicts, as a pear channel could be the same as another packages vendor name for example, then the channel short name (or full URL) can be used to reference which channel the package is in.
When this code is installed it will be available in your vendor directory and automatically available through the Composer autoloader:
vendor/pear2/pear2-http-request/pear2/HTTP/Request.php
To use this PEAR package simply reference it like so:
1 <?php
2 require __DIR__ . '/vendor/autoload.php';
3
4 use PEAR2\HTTP\Request;
5
6 $request = new Request();
7.8 Composer and Packagist
Composer is the recommended dependency manager for PHP. List your project’s dependencies in a composer.json file and,
with a few simple commands, Composer will automatically download your project’s dependencies and setup autoloading for
you. Composer is analogous to NPM in the node.js world, or Bundler in the Ruby world.
There is a plethora of PHP libraries that are compatible with Composer and ready to be used in your project. These “packages” are listed on Packagist, the official repository for Composer-compatible PHP libraries.
How to Install Composer
The safest way to download composer is by following the official instructions.
This will verify the installer is not corrupt or tampered with.
The installer installs a composer.phar binary in your current working directory.
We recommend installing Composer globally (e.g. a single copy in /usr/local/bin). To do so, run this command next:
1 mv composer.phar /usr/local/bin/composer
Note: If the above fails due to permissions, prefix with sudo.
To run a locally installed Composer you’d use php composer.phar, globally it’s simply composer.
Installing on Windows
For Windows users the easiest way to get up and running is to use the ComposerSetup installer, which
performs a global install and sets up your $PATH so that you can just call composer from any
directory in your command line.
How to Define and Install Dependencies
Composer keeps track of your project’s dependencies in a file called composer.json. You can manage it
by hand if you like, or use Composer itself. The composer require command adds a project dependency
and if you don’t have a composer.json file, one will be created. Here’s an example that adds Twig
as a dependency of your project.
1 composer require twig/twig:^2.0
Alternatively, the composer init command will guide you through creating a full composer.json file
for your project. Either way, once you’ve created your composer.json file you can tell Composer to
download and install your dependencies into the vendor/ directory. This also applies to projects
you’ve downloaded that already provide a composer.json file:
1 composer install
Next, add this line to your application’s primary PHP file; this will tell PHP to use Composer’s autoloader for your project dependencies.
1 <?php
2 require 'vendor/autoload.php';
Now you can use your project dependencies, and they’ll be autoloaded on demand.
Updating your dependencies
Composer creates a file called composer.lock which stores the exact version of each package it
downloaded when you first ran composer install. If you share your project with others,
ensure the composer.lock file is included, so that when they run composer install they’ll
get the same versions as you. To update your dependencies, run composer update. Don’t use
composer update when deploying, only composer install, otherwise you may end up with different
package versions on production.
This is most useful when you define your version requirements flexibly. For instance, a version
requirement of ~1.8 means “anything newer than 1.8.0, but less than 2.0.x-dev”. You can also use
the * wildcard as in 1.8.*. Now Composer’s composer update command will upgrade all your
dependencies to the newest version that fits the restrictions you define.
Update Notifications
To receive notifications about new version releases you can sign up for libraries.io, a web service that can monitor dependencies and send you alerts on updates.
Checking your dependencies for security issues
The Local PHP Security Checker is a command-line tool, which will examine your composer.lock
file and tell you if you need to update any of your dependencies.
Handling global dependencies with Composer
Composer can also handle global dependencies and their binaries. Usage is straight-forward, all you need
to do is prefix your command with global. If for example you wanted to install PHPUnit and have it
available globally, you’d run the following command:
1 composer global require phpunit/phpunit
This will create a ~/.composer folder where your global dependencies reside. To have the installed
packages’ binaries available everywhere, you’d then add the ~/.composer/vendor/bin folder to your
$PATH variable.