Merge branch 'gh-pages' into patch-ar

This commit is contained in:
Peter Kokot
2017-01-23 06:08:22 +01:00
committed by GitHub
32 changed files with 629 additions and 215 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
/_site/
*.DS_Store
node_modules
vendor
.bundle

View File

@@ -66,7 +66,7 @@ included in the project:
```bash
# Install the needed gems through Bundler
bundle install
bundle install --path vendor/bundle
# Run the local server
bundle exec jekyll serve
```

View File

@@ -1,2 +1,3 @@
source 'https://rubygems.org'
gem 'github-pages'
gem 'rouge'

View File

@@ -2,125 +2,146 @@ GEM
remote: https://rubygems.org/
specs:
RedCloth (4.2.9)
activesupport (4.2.5.1)
activesupport (5.0.0.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.3.8)
addressable (2.4.0)
blankslate (2.1.2.4)
classifier-reborn (2.0.4)
fast-stemmer (~> 1.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.10.0)
colorator (0.1)
ethon (0.8.1)
concurrent-ruby (1.0.2)
ethon (0.9.1)
ffi (>= 1.3.0)
execjs (2.6.0)
faraday (0.9.2)
execjs (2.7.0)
faraday (0.10.0)
multipart-post (>= 1.2, < 3)
ffi (1.9.10)
fast-stemmer (1.0.2)
ffi (1.9.14)
ffi (1.9.14-x64-mingw32)
gemoji (2.1.0)
github-pages (48)
github-pages (39)
RedCloth (= 4.2.9)
github-pages-health-check (= 0.6.1)
jekyll (= 3.0.3)
github-pages-health-check (~> 0.2)
jekyll (= 2.4.0)
jekyll-coffeescript (= 1.0.1)
jekyll-feed (= 0.3.1)
jekyll-gist (= 1.4.0)
jekyll-mentions (= 1.0.0)
jekyll-paginate (= 1.1.0)
jekyll-redirect-from (= 0.9.1)
jekyll-mentions (= 0.2.1)
jekyll-redirect-from (= 0.8.0)
jekyll-sass-converter (= 1.3.0)
jekyll-seo-tag (= 1.0.0)
jekyll-sitemap (= 0.10.0)
jekyll-textile-converter (= 0.1.0)
jemoji (= 0.5.1)
kramdown (= 1.9.0)
liquid (= 3.0.6)
jekyll-sitemap (= 0.8.1)
jemoji (= 0.5.0)
kramdown (= 1.5.0)
liquid (= 2.6.2)
maruku (= 0.7.0)
mercenary (~> 0.3)
rdiscount (= 2.1.8)
redcarpet (= 3.3.3)
rouge (= 1.10.1)
pygments.rb (= 0.6.3)
rdiscount (= 2.1.7)
redcarpet (= 3.3.2)
terminal-table (~> 1.4)
github-pages-health-check (0.6.1)
addressable (~> 2.3)
net-dns (~> 0.8)
github-pages-health-check (0.3.2)
net-dns (~> 0.6)
public_suffix (~> 1.4)
typhoeus (~> 0.7)
html-pipeline (2.3.0)
activesupport (>= 2, < 5)
nokogiri (>= 1.4)
html-pipeline (1.9.0)
activesupport (>= 2)
nokogiri (~> 1.4)
i18n (0.7.0)
jekyll (3.0.3)
jekyll (2.4.0)
classifier-reborn (~> 2.0)
colorator (~> 0.1)
jekyll-coffeescript (~> 1.0)
jekyll-gist (~> 1.0)
jekyll-paginate (~> 1.0)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 1.1)
kramdown (~> 1.3)
liquid (~> 3.0)
liquid (~> 2.6.1)
mercenary (~> 0.3.3)
rouge (~> 1.7)
pygments.rb (~> 0.6.0)
redcarpet (~> 3.1)
safe_yaml (~> 1.0)
toml (~> 0.1.0)
jekyll-coffeescript (1.0.1)
coffee-script (~> 2.2)
jekyll-feed (0.3.1)
jekyll-gist (1.4.0)
octokit (~> 4.2)
jekyll-mentions (1.0.0)
html-pipeline (~> 2.2)
jekyll (~> 3.0)
jekyll-mentions (0.2.1)
html-pipeline (~> 1.9.0)
jekyll (~> 2.0)
jekyll-paginate (1.1.0)
jekyll-redirect-from (0.9.1)
jekyll-redirect-from (0.8.0)
jekyll (>= 2.0)
jekyll-sass-converter (1.3.0)
sass (~> 3.2)
jekyll-seo-tag (1.0.0)
jekyll (>= 2.0)
jekyll-sitemap (0.10.0)
jekyll-textile-converter (0.1.0)
RedCloth (~> 4.0)
jekyll-watch (1.3.1)
listen (~> 3.0)
jemoji (0.5.1)
jekyll-sitemap (0.8.1)
jekyll-watch (1.5.0)
listen (~> 3.0, < 3.1)
jemoji (0.5.0)
gemoji (~> 2.0)
html-pipeline (~> 2.2)
html-pipeline (~> 1.9)
jekyll (>= 2.0)
json (1.8.3)
kramdown (1.9.0)
liquid (3.0.6)
listen (3.0.6)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9.7)
mercenary (0.3.5)
mini_portile2 (2.0.0)
minitest (5.8.4)
kramdown (1.5.0)
liquid (2.6.2)
listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
maruku (0.7.0)
mercenary (0.3.6)
mini_portile2 (2.1.0)
minitest (5.9.1)
multipart-post (2.0.0)
net-dns (0.8.0)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
octokit (4.2.0)
sawyer (~> 0.6.0, >= 0.5.3)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
nokogiri (1.6.8.1-x64-mingw32)
mini_portile2 (~> 2.1.0)
octokit (4.6.1)
sawyer (~> 0.8.0, >= 0.5.3)
parslet (1.5.0)
blankslate (~> 2.0)
posix-spawn (0.3.12)
public_suffix (1.5.3)
rb-fsevent (0.9.7)
pygments.rb (0.6.3)
posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.2.0)
rb-fsevent (0.9.8)
rb-inotify (0.9.7)
ffi (>= 0.5.0)
rdiscount (2.1.8)
redcarpet (3.3.3)
rouge (1.10.1)
rdiscount (2.1.7)
redcarpet (3.3.2)
rouge (2.0.7)
safe_yaml (1.0.4)
sass (3.4.21)
sawyer (0.6.0)
addressable (~> 2.3.5)
faraday (~> 0.8, < 0.10)
terminal-table (1.5.2)
sass (3.4.22)
sawyer (0.8.1)
addressable (>= 2.3.5, < 2.6)
faraday (~> 0.8, < 1.0)
terminal-table (1.7.3)
unicode-display_width (~> 1.1.1)
thread_safe (0.3.5)
toml (0.1.2)
parslet (~> 1.5.0)
typhoeus (0.8.0)
ethon (>= 0.8.0)
tzinfo (1.2.2)
thread_safe (~> 0.1)
unicode-display_width (1.1.1)
yajl-ruby (1.2.1)
PLATFORMS
ruby
x64-mingw32
DEPENDENCIES
github-pages
rouge
BUNDLED WITH
1.13.6

View File

@@ -18,6 +18,8 @@ developers know where to find good information!
## How to Contribute
You should read the `CONTRIBUTING.md` file for precise instructions and tips. But, if you prefer a TL;DR:
1. Fork and edit
2. Optionally install [Ruby](https://rvm.io/rvm/install/) with [Jekyll](https://github.com/mojombo/jekyll/) gem to preview locally
3. Submit pull request for consideration

View File

@@ -19,6 +19,6 @@ defaults:
values:
sitemap: false
exclude: ['CNAME', 'CONTRIBUTING.md', 'LICENSE', 'README.md', 'pages/example.md']
exclude: ['CNAME', 'CONTRIBUTING.md', 'LICENSE', 'README.md', 'pages/example.md', 'vendor']
future: true

View File

@@ -43,16 +43,21 @@ _PHP: The Right Way_ is translated into many different languages:
* [简体中文](http://laravel-china.github.io/php-the-right-way/)
* [繁體中文](http://laravel-taiwan.github.io/php-the-right-way)
## Book
The most recent version of _PHP: The Right Way_ is also available in PDF, EPUB and MOBI formats. [Go to Leanpub][1]
## How to Contribute
Help make this website the best resource for new PHP programmers! [Contribute on GitHub][1]
Help make this website the best resource for new PHP programmers! [Contribute on GitHub][2]
## Spread the Word!
_PHP: The Right Way_ has web banner images you can use on your website. Show your support, and let new PHP developers
know where to find good information!
[See Banner Images][2]
[See Banner Images][3]
[1]: https://github.com/codeguy/php-the-right-way/tree/gh-pages
[2]: /banners.html
[1]: https://leanpub.com/phptherightway
[2]: https://github.com/codeguy/php-the-right-way/tree/gh-pages
[3]: /banners.html

View File

@@ -1,16 +1,16 @@
---
title: Use the Current Stable Version (7.0)
title: Use the Current Stable Version (7.1)
isChild: true
anchor: use_the_current_stable_version
---
## Use the Current Stable Version (7.0) {#use_the_current_stable_version_title}
## Use the Current Stable Version (7.1) {#use_the_current_stable_version_title}
If you are getting started with PHP, start with the current stable release of [PHP 7.0][php-release]. PHP 7.0 is very
If you are getting started with PHP, start with the current stable release of [PHP 7.1][php-release]. PHP 7.1 is very
new, and adds many amazing [new features](#language_highlights) over the older 5.x versions. The engine has been largely re-written, and PHP is now even quicker than older versions.
Most commonly in the near future you will find PHP 5.x being used, and the latest 5.x version is 5.6. This is not a bad option, but you should try to upgrade to the latest stable quickly - PHP 5.6 [will not receive security updates beyond 2018](http://php.net/supported-versions.php). Upgrading is really quite easy, as there are not many [backwards compatibility breaks][php70-bc]. If you are not sure which version a function or feature is in, you can check the PHP documentation on the [php.net][php-docs] website.
Most commonly in the near future you will find PHP 5.x being used, and the latest 5.x version is 5.6. This is not a bad option, but you should try to upgrade to the latest stable quickly - PHP 5.6 [will not receive security updates beyond 2018](http://php.net/supported-versions.php). Upgrading is really quite easy, as there are not many [backwards compatibility breaks][php71-bc]. If you are not sure which version a function or feature is in, you can check the PHP documentation on the [php.net][php-docs] website.
[php-release]: http://php.net/downloads.php
[php-docs]: http://php.net/manual/
[php70-bc]: http://php.net/manual/migration70.incompatible.php
[php71-bc]: http://php.net/manual/migration71.incompatible.php

View File

@@ -15,7 +15,7 @@ There are multiple ways to install PHP on OS X.
[Homebrew] is a powerful package manager for OS X, which can help you install PHP and various extensions easily.
[Homebrew PHP] is a repository that contains PHP-related "formulae" for Homebrew, and will let you install PHP.
At this point, you can install `php53`, `php54`, `php55`, `php56` or `php70` using the `brew install` command, and switch
At this point, you can install `php53`, `php54`, `php55`, `php56`, `php70` or `php71` using the `brew install` command, and switch
between them by modifying your `PATH` variable. Alternatively you can use [brew-php-switcher][brew-php-switcher] which will switch automatically for you.
### Install PHP via Macports
@@ -26,7 +26,7 @@ command-line, X11 or Aqua based open-source software on the OS X operating
system.
MacPorts supports pre-compiled binaries, so you don't need to recompile every
dependencies from the source tarball files, it saves your life if you don't
dependency from the source tarball files, it saves your life if you don't
have any package installed on your system.
At this point, you can install `php54`, `php55`, `php56` or `php70` using the `port install` command, for example:
@@ -34,7 +34,7 @@ At this point, you can install `php54`, `php55`, `php56` or `php70` using the `p
sudo port install php56
sudo port install php70
And you can run `select` command to switch your active php:
And you can run `select` command to switch your active PHP:
sudo port select --set php php70
@@ -46,7 +46,7 @@ applications/projects require different versions of PHP, and you are not using v
### Install PHP via Liip's binary installer
Another popular option is [php-osx.liip.ch] which provides one liner installation methods for versions 5.3 through 7.0.
It doesn't overwrite the php binaries installed by Apple, but installs everything in a separate location (/usr/local/php5).
It doesn't overwrite the PHP binaries installed by Apple, but installs everything in a separate location (/usr/local/php5).
### Compile from Source

View File

@@ -7,13 +7,13 @@ anchor: windows_setup
You can download the binaries from [windows.php.net/download][php-downloads]. After the extraction of PHP, it is recommended to set the [PATH][windows-path] to the root of your PHP folder (where php.exe is located) so you can execute PHP from anywhere.
For learning and local development you can use the built in webserver with PHP 5.4+ so you don't need to worry about
For learning and local development, you can use the built in webserver with PHP 5.4+ so you don't need to worry about
configuring it. If you would like an "all-in-one" which includes a full-blown webserver and MySQL too then tools such
as the [Web Platform Installer][wpi], [XAMPP][xampp], [EasyPHP][easyphp], [OpenServer][openserver] and [WAMP][wamp] will
help get a Windows development environment up and running fast. That said, these tools will be a little different from
production so be careful of environment differences if you are working on Windows and deploying to Linux.
If you need to run your production system on Windows then IIS7 will give you the most stable and best performance. You
If you need to run your production system on Windows, then IIS7 will give you the most stable and best performance. You
can use [phpmanager][phpmanager] (a GUI plugin for IIS7) to make configuring and managing PHP simple. IIS7 comes with
FastCGI built in and ready to go, you just need to configure PHP as a handler. For support and additional resources
there is a [dedicated area on iis.net][php-iis] for PHP.
@@ -30,6 +30,6 @@ Chris Tankersley has a very helpful blog post on what tools he uses to do [PHP d
[php-downloads]: http://windows.php.net/download/
[php-iis]: http://php.iis.net/
[windows-path]: http://www.windows-commandline.com/set-path-command-line/
[windows-tools]: http://ctankersley.com/2015/07/01/developing-on-windows/
[wpi]: http://www.microsoft.com/web/downloads/platform.aspx
[windows-tools]: http://ctankersley.com/2016/11/13/developing-on-windows-2016/
[wpi]: https://www.microsoft.com/web/downloads/platform.aspx
[xampp]: http://www.apachefriends.org/en/xampp.html

View File

@@ -78,7 +78,7 @@ as a dependency of your project.
composer require twig/twig:~1.8
{% endhighlight %}
Alternatively the `composer init` command will guide you through creating a full `composer.json` file
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:
@@ -100,12 +100,13 @@ 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 other coders and the `composer.lock` file
is part of your distribution, when they run `composer install` they'll get the same versions as you.
To update your dependencies, run `composer update`.
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
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.

View File

@@ -58,7 +58,7 @@ handle your PEAR dependencies. This example will install code from `pear2.php.ne
{% endhighlight %}
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:
terminology) the pear repo. Then the `require` section will prefix the package name like this:
> pear-channel/Package

View File

@@ -6,7 +6,7 @@ anchor: the_basics
## The Basics {#the_basics_title}
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
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.

View File

@@ -18,7 +18,7 @@ for a brief, practical summary.
### UTF-8 at the PHP level
The basic string operations, like concatenating two strings and assigning strings to variables, don't need anything
special for UTF-8. However most string functions, like `strpos()` and `strlen()`, do need special consideration. These
special for UTF-8. However, most string functions, like `strpos()` and `strlen()`, do need special consideration. These
functions often have an `mb_*` counterpart: for example, `mb_strpos()` and `mb_strlen()`. These `mb_*` strings are made
available to you via the [Multibyte String Extension], and are specifically designed to operate on Unicode strings.

View File

@@ -0,0 +1,416 @@
---
title: Internationalization and Localization
isChild: true
anchor: i18n_l10n
---
## Internationalization (i18n) and Localization (l10n) {#i18n_l10n_title}
_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 is usually done once - preferably, in the beginning of the project, or else you'll 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 needed between different languages to interoperate strings containing numbers and
counters. For instance, in English when you have only one item, it's 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.
## 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 is, however, hardly a recommended way 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`][gettext]. It dates
back to 1995 and is still a complete implementation for translating software. It is pretty easy to get running, while
it still sports powerful supporting tools. It's 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
files.
### 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:
- [oscarotero/Gettext][oscarotero]: Gettext support with an OO interface; includes improved helper functions, powerful
extractors for several file formats (some of them not supported natively by the `gettext` command), and can also export
to other formats besides `.mo/.po` files. Can be useful if you need to integrate your translation files into other parts
of the system, like a JavaScript interface.
- [symfony/translation][symfony]: 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.
- [zend/i18n][zend]: 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 `@lang` helper for template files.
- [Yii] supports array, Gettext, and database-based translation, and includes a messages extractor. It is backed by the
[`Intl`][intl] extension, 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.
## 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's available for Unix, Mac, and Windows, and can be [downloaded for free on their website][poedit_download]
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, that 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're using to do l10n, you can go just fine with only PO/MO files.
You'll 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're 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's defined following the [ISO 639-1][639-1] and
[ISO 3166-1 alpha-2][3166-1] 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][rare], 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's not present, it's 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'll need to select an arbitrary
root for your l10n files in your source repository. Inside it, you'll have a folder for each needed locale, and a fixed
`LC_MESSAGES` folder that will contain all your PO/MO pairs. Example:
{% highlight console %}
<project root>
├─ src/
├─ templates/
└─ locales/
├─ forum.pot
├─ site.pot
├─ de/
│ └─ LC_MESSAGES/
│ ├─ forum.mo
│ ├─ forum.po
│ ├─ site.mo
│ └─ site.po
├─ es_ES/
│ └─ LC_MESSAGES/
│ └─ ...
├─ fr/
│ └─ ...
├─ pt_BR/
│ └─ ...
└─ pt_PT/
└─ ...
{% endhighlight %}
### 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'll have to declare the [plural rules][plural] 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'll 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][lingohub_plurals] -, you might want to copy the ones you need from a [list][plural] instead
of writing them by hand.
When calling out Gettext to do localization on sentences with counters, you'll have to give him 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 instead the overall content, you'll learn how to edit it easily later:
{% highlight po %}
msgid ""
msgstr ""
"Language: pt_BR\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "We're now translating some strings"
msgstr "Nós estamos traduzindo algumas strings agora"
msgid "Hello %1$s! Your last visit was on %2$s"
msgstr "Olá %1$s! Sua última visita foi em %2$s"
msgid "Only one unread message"
msgid_plural "%d unread messages"
msgstr[0] "Só uma mensagem não lida"
msgstr[1] "%d mensagens não lidas"
{% endhighlight %}
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`][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's
advised to not use a complex language as the source of translation.
### Discussion on l10n keys
As you might have noticed, we're 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:
1. _`msgid` as 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's much easier for the translator to understand what's going on and make 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 `msgid`
across several language files.
2. _`msgid` as 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's 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.po` file, that
translators would read to understand what to write in `fr.po` for instance.
- missing translations would display meaningless keys on screen (`top_menu.welcome` instead of `Hello there, User!`
on the said untranslated French page). That's good it as would force translation to be complete before publishing -
but bad as translation issues would be really 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][manual] favors the first approach as, in general, it's easier for translators and users in
case of trouble. That's how we will be working here as well. However, the [Symfony documentation][symfony-keys] favors
keyword-based translation, to allow for independent changes of all translations without affecting templates as well.
### Everyday usage
In a common 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
{% highlight php %}
<?php include 'i18n_setup.php' ?>
<div id="header">
<h1><?=sprintf(gettext('Welcome, %s!'), $name)?></h1>
<!-- code indented this way only for legibility -->
<?php if ($unread): ?>
<h2><?=sprintf(
ngettext('Only one unread message',
'%d unread messages',
$unread),
$unread)?>
</h2>
<?php endif ?>
</div>
<h1><?=gettext('Introduction')?></h1>
<p><?=gettext('We\'re now translating some strings')?></p>
{% endhighlight %}
- [`gettext()`][func] simply translates a `msgid` into its corresponding `msgstr` for a given language. There's also
the shorthand function `_()` that works the same way;
- [`ngettext()`][n_func] does the same but with plural rules;
- there's also [`dgettext()`][d_func] and [`dngettext()`][dn_func], that allows 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
{% highlight php %}
<?php
/**
* Verifies if the given $locale is supported in the project
* @param string $locale
* @return bool
*/
function valid($locale) {
return in_array($locale, ['en_US', 'en', 'pt_BR', 'pt', 'es_ES', 'es');
}
//setting the source/default locale, for informational purposes
$lang = 'en_US';
if (isset($_GET['lang']) && valid($_GET['lang'])) {
// the locale can be changed through the query-string
$lang = $_GET['lang']; //you should sanitize this!
setcookie('lang', $lang); //it's stored in a cookie so it can be reused
} elseif (isset($_COOKIE['lang']) && valid($_COOKIE['lang'])) {
// if the cookie is present instead, let's just keep it
$lang = $_COOKIE['lang']; //you should sanitize this!
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
// default: look for the languages the browser says the user accepts
$langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
array_walk($langs, function (&$lang) { $lang = strtr(strtok($lang, ';'), ['-' => '_']); });
foreach ($langs as $browser_lang) {
if (valid($browser_lang)) {
$lang = $browser_lang;
break;
}
}
}
// here we define the global system locale given the found language
putenv("LANG=$lang");
// this might be useful for date functions (LC_TIME) or money formatting (LC_MONETARY), for instance
setlocale(LC_ALL, $lang);
// this will make Gettext look for ../locales/<lang>/LC_MESSAGES/main.mo
bindtextdomain('main', '../locales');
// indicates in what encoding the file should be read
bind_textdomain_codeset('main', 'UTF-8');
// if your application has additional domains, as cited before, you should bind them here as well
bindtextdomain('forum', '../locales');
bind_textdomain_codeset('forum', 'UTF-8');
// here we indicate the default domain the gettext() calls will respond to
textdomain('main');
// this would look for the string in forum.mo instead of main.mo
// echo dgettext('forum', 'Welcome back!');
?>
{% endhighlight %}
#### 3. Preparing translation for the first run
To make matters easier - and one of the powerful advantages Gettext has over custom framework i18n packages - is its
custom file type. "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][poedit_download], 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 powerful features Gettext has available.
In the first run, you should select "File > New Catalog" from the menu. There you'll have a small screen where we will
set the terrain so everything else runs smoothly. You'll be able to find those settings later through
"Catalog > Properties":
- Project name and version, Translation Team and email address: useful information that goes in the `.po` file header;
- Language: here you should use that format we mentioned before, such as `en_US` or `pt_BR`;
- Charsets: UTF-8, preferably;
- Source charset: set here the charset used by your PHP files - probably UTF-8 as well, right?
- plural forms: here go those rules we mentioned before - there's a link in there with samples as well;
- Source paths: here you must include all folders from the project where `gettext()` (and siblings) will happen - this
is usually your templates folder(s)
- Source keywords: this last part is filled by default, but you might need to alter it later - and is one of the
powerful points of Gettext. The underlying software knows how the `gettext()` calls look like in several programming
languages, but you might as well create your own translation forms. This will be discussed later in the "Tips" section.
After setting those points you'll be prompted to save the file - using that directory structure we mentioned as well,
and then it will run a scan through your source files to find the localization calls. They'll be 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 the ones with plural
forms. The first ones have simply two boxes: source and localized string. The source string can't 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, being highlighted in the list. It's also useful if you have a translation team and someone
tries to write something they're not sure about: just mark Fuzzy and someone else will review later.
Finally, it's 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're 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's 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's 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's 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
[oscarotero's Gettext][oscarotero] 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's very easy. It's just a field in the `.po` file, or a Settings screen on Poedit. In the editor,
that option is inside "Catalog > Properties > Source keywords". You need to include there the specifications of those
new functions, following [a specific format][func_format]:
- if you create something like `t()` that simply returns the translation for a string, you can specify it as `t`.
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](https://en.wikipedia.org/wiki/Internationalization_and_localization)
* [Wikipedia: Gettext](https://en.wikipedia.org/wiki/Gettext)
* [LingoHub: PHP internationalization with gettext tutorial][lingohub]
* [PHP Manual: Gettext](http://php.net/manual/en/book.gettext.php)
* [Gettext Manual][manual]
[Poedit]: https://poedit.net
[poedit_download]: https://poedit.net/download
[lingohub]: https://lingohub.com/blog/2013/07/php-internationalization-with-gettext-tutorial/
[lingohub_plurals]: https://lingohub.com/blog/2013/07/php-internationalization-with-gettext-tutorial/#Plurals
[plural]: http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html
[gettext]: https://en.wikipedia.org/wiki/Gettext
[manual]: http://www.gnu.org/software/gettext/manual/gettext.html
[639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
[3166-1]: http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
[rare]: http://www.gnu.org/software/gettext/manual/gettext.html#Rare-Language-Codes
[func_format]: https://www.gnu.org/software/gettext/manual/gettext.html#Language-specific-options
[oscarotero]: https://github.com/oscarotero/Gettext
[symfony]: https://symfony.com/doc/current/components/translation.html
[zend]: https://docs.zendframework.com/zend-i18n/translation
[laravel]: https://laravel.com/docs/master/localization
[yii]: http://www.yiiframework.com/doc-2.0/guide-tutorial-i18n.html
[intl]: http://br2.php.net/manual/en/intro.intl.php
[ICU project]: http://www.icu-project.org
[symfony-keys]: https://symfony.com/doc/current/components/translation/usage.html#creating-translations
[sprintf]: http://php.net/manual/en/function.sprintf.php
[func]: http://php.net/manual/en/function.gettext.php
[n_func]: http://php.net/manual/en/function.ngettext.php
[d_func]: http://php.net/manual/en/function.dgettext.php
[dn_func]: http://php.net/manual/en/function.dngettext.php

View File

@@ -10,7 +10,7 @@ If you have ever read about Dependency Injection then you have probably seen the
### Inversion of Control
Inversion of Control is as it says, "inverting the control" of a system by keeping organisational control entirely
Inversion of Control is as it says, "inverting the control" of a system by keeping organizational control entirely
separate from our objects. In terms of Dependency Injection, this means loosening our dependencies by controlling and
instantiating them elsewhere in the system.

View File

@@ -19,7 +19,7 @@ foreach ($db->query('SELECT * FROM table') as $row) {
</ul>
{% endhighlight %}
This is bad practice for all sorts of reasons, mainly that its hard to debug, hard to test, hard to read and it is
This is bad practice for all sorts of reasons, mainly that it's hard to debug, hard to test, hard to read and it is
going to output a lot of fields if you don't put a limit on there.
While there are many other solutions to doing this - depending on if you prefer [OOP](/#object-oriented-programming) or

View File

@@ -131,9 +131,8 @@ PHP is perfectly capable of being an "exception-heavy" programming language, and
make the switch. Basically you can throw your "errors" as "exceptions" using the `ErrorException` class, which extends
the `Exception` class.
This is a common practice implemented by a large number of modern frameworks such as Symfony and Laravel. By default
Laravel will display all errors as exceptions using the [Whoops!] package if the `app.debug` switch is turned on, then
hide them if the switch is turned off.
This is a common practice implemented by a large number of modern frameworks such as Symfony and Laravel. In debug
mode *(or dev mode)* both of these frameworks will display a nice and clean *stack trace*.
By throwing errors as exceptions in development you can handle them better than the usual result, and if you see an
exception during development you can wrap it in a catch statement with specific instructions on how to handle the

View File

@@ -8,12 +8,17 @@ anchor: password_hashing
Eventually everyone builds a PHP application that relies on user login. Usernames and passwords are stored in a
database and later used to authenticate users upon login.
It is important that you properly [_hash_][3] passwords before storing them. Password hashing is an irreversible, one
way function performed against the user's password. This produces a fixed-length string that cannot be feasibly
It is important that you properly [_hash_][3] passwords before storing them. Password hashing is an irreversible,
one-way function performed against the user's password. This produces a fixed-length string that cannot be feasibly
reversed. This means you can compare a hash against another to determine if they both came from the same source string,
but you cannot determine the original string. If passwords are not hashed and your database is accessed by an
unauthorized third-party, all user accounts are now compromised. Some users may (unfortunately) use the same password
for other services. Therefore, it is important to take security seriously.
unauthorized third-party, all user accounts are now compromised.
Passwords should also be individually [_salted_][5] by adding a random string to each password before hashing. This prevents dictionary attacks and the use of "rainbow tables" (a reverse list of crytographic hashes for common passwords.)
Hashing and salting are vital as often users use the same password for multiple services and password quality can be poor.
Fortunately, nowadays PHP makes this easy.
**Hashing passwords with `password_hash`**
@@ -37,10 +42,12 @@ if (password_verify('bad-password', $passwordHash)) {
}
{% endhighlight %}
`password_hash()` takes care of password salting for you. The salt is stored, along with the algorithm and "cost", as part of the hash. `password_verify()` extracts this to determine how to check the password, so you don't need a separate database field to store your salts.
* [Learn about `password_hash()`] [1]
* [`password_compat` for PHP >= 5.3.7 && < 5.5] [2]
* [Learn about hashing in regards to cryptography] [3]
* [Learn about salts] [5]
* [PHP `password_hash()` RFC] [4]
@@ -48,3 +55,4 @@ if (password_verify('bad-password', $passwordHash)) {
[2]: https://github.com/ircmaxell/password_compat
[3]: http://en.wikipedia.org/wiki/Cryptographic_hash_function
[4]: https://wiki.php.net/rfc/password_hash
[5]: https://en.wikipedia.org/wiki/Salt_(cryptography)

View File

@@ -14,4 +14,4 @@ via the file system.
that, even if the script is accessed directly, it will not be output as plain text.
- Information in configuration files should be protected accordingly, either through encryption or group/user file
system permissions.
- It is a good idea to ensure that you do not commit configuration files containing sensitive information eg passwords or API tokens to source control.
- It is a good idea to ensure that you do not commit configuration files containing sensitive information e.g. passwords or API tokens to source control.

View File

@@ -35,10 +35,14 @@ Alternatively, if you want to squeeze more performance and stability out of Apac
same FPM system as nginx and run the [worker MPM] or [event MPM] with mod_fastcgi or mod_fcgid. This configuration will
be significantly more memory efficient and much faster but it is more work to set up.
If you are running Apache 2.4 or later, you can use [mod_proxy_fcgi] to get great performance that is easy to setup.
* [Read more on Apache][apache]
* [Read more on Multi-Processing Modules][apache-MPM]
* [Read more on mod_fastcgi][mod_fastcgi]
* [Read more on mod_fcgid][mod_fcgid]
* [Read more on mod_proxy_fcgi][mod_proxy_fcgi]
* [Read more on setting up Apache and PHP-FPM with mod_proxy_fcgi][tutorial-mod_proxy_fcgi]
[nginx]: http://nginx.org/
@@ -50,5 +54,7 @@ be significantly more memory efficient and much faster but it is more work to se
[event MPM]: http://httpd.apache.org/docs/2.4/mod/event.html
[apache]: http://httpd.apache.org/
[apache-MPM]: http://httpd.apache.org/docs/2.4/mod/mpm_common.html
[mod_fastcgi]: http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html
[mod_fastcgi]: https://blogs.oracle.com/opal/entry/php_fpm_fastcgi_process_manager
[mod_fcgid]: http://httpd.apache.org/mod_fcgid/
[mod_proxy_fcgi]: https://httpd.apache.org/docs/current/mod/mod_proxy_fcgi.html
[tutorial-mod_proxy_fcgi]: https://serversforhackers.com/video/apache-and-php-fpm

View File

@@ -9,6 +9,6 @@ PHP has shared servers to thank for its popularity. It is hard to find a host wi
the latest version. Shared servers allow you and other developers to deploy websites to a single machine. The upside to
this is that it has become a cheap commodity. The downside is that you never know what kind of a ruckus your
neighboring tenants are going to create; loading down the server or opening up security holes are the main concerns. If
your project's budget can afford to avoid shared servers you should.
your project's budget can afford to avoid shared servers, you should.
To make sure your shared servers are offering the latest versions of PHP, check out [PHP Versions](http://phpversions.info/shared-hosting/).

View File

@@ -28,13 +28,13 @@ There are many open source tools available to help you with build automation and
[Phing] can control your packaging, deployment or testing process from within a XML build file. Phing (which is based on [Apache Ant]) provides a rich set of tasks usually needed to install or update a web application and can be extended with additional custom tasks, written in PHP. It's a solid and robust tool and has been around for a long time, however the tool could be perceived as a bit old fashioned because of the way it deals with configuration (XML files).
[Capistrano] is a system for *intermediate-to-advanced programmers* to execute commands in a structured, repeatable way on one or more remote machines. It is pre-configured for deploying Ruby on Rails applications, however you can successfully deploying PHP systems with it. Successful use of Capistrano depends on a working knowledge of Ruby and Rake. Dave Gardner's blog post [PHP Deployment with Capistrano][phpdeploy_capistrano] is a good starting point for PHP developers interested in Capistrano.
[Capistrano] is a system for *intermediate-to-advanced programmers* to execute commands in a structured, repeatable way on one or more remote machines. It is pre-configured for deploying Ruby on Rails applications, however you can successfully deploy PHP systems with it. Successful use of Capistrano depends on a working knowledge of Ruby and Rake. Dave Gardner's blog post [PHP Deployment with Capistrano][phpdeploy_capistrano] is a good starting point for PHP developers interested in Capistrano.
[Rocketeer] gets its inspiration and philosophy from the Laravel framework. Its goal is to be fast, elegant and ease to use with smart defaults. It features multiple servers, multiple stages, atomic deploys and deployment can be performed in parallel. Everything in the tool can be hot swapped or extended, and everything is written in PHP.
[Rocketeer] gets its inspiration and philosophy from the Laravel framework. Its goal is to be fast, elegant and easy to use with smart defaults. It features multiple servers, multiple stages, atomic deploys and deployment can be performed in parallel. Everything in the tool can be hot swapped or extended, and everything is written in PHP.
[Deployer] is a deployment tool written in PHP, it's simple and functional. Runs tasks in parallel, atomic deployment, keeps consistency between servers. Recipes of common tasks for Symfony, Laravel, Zend Framework and Yii. Younes Rafie's article [Easy Deployment of PHP Applications with Deployer][phpdeploy_deployer] is a great tutorial for deploying your application with the tool.
[Deployer] is a deployment tool written in PHP. It's simple and functional. Features include running tasks in parallel, atomic deployment and keeping consistency between servers. Recipes of common tasks for Symfony, Laravel, Zend Framework and Yii are available. Younes Rafie's article [Easy Deployment of PHP Applications with Deployer][phpdeploy_deployer] is a great tutorial for deploying your application with the tool.
[Magallanes] another tool written in PHP with simple configuration done in YAML files. It has support for multiple servers and environments, atomic deployment, and have some built in tasks that you can leverage for common tools and frameworks.
[Magallanes] is another tool written in PHP with simple configuration done in YAML files. It has support for multiple servers and environments, atomic deployment, and has some built in tasks that you can leverage for common tools and frameworks.
#### Further reading:

View File

@@ -5,41 +5,40 @@ anchor: docker
## Docker {#docker_title}
Beside using Vagrant, another easy way to get a virtual development or production environment up and running is [Docker].
Docker helps you to provide Linux containers for all kind of applications.
There are many helpful docker images which could provide you with other great services without the need to install
these services on your local machine, e.g. MySQL or PostgreSQL and a lot more. Have a look at the [Docker Hub Registry]
[docker-hub] to search a list of available pre-built containers, which you can then run and use in very few steps.
[Docker] - a lightweight alternative to a full virtual machine - is so called because it's all about "containers". A container is a building block which, in the simplest case, does one specific job, e.g. running a web server. An "image" is the package you use to build the container - Docker has a repository full of them.
A typical LAMP application might have three containers: a web server, a PHP-FPM process and MySQL. As with shared folders in Vagrant, you can leave your application files where they are and tell Docker where to find them.
You can generate containers from the command line (see example below) or, for ease of maintenance, build a `docker-compose.yml` file for your project specifying which to create and how they communicate with one another.
Docker may help if you're developing multiple websites and want the separation that comes from installing each on it's own virtual machine, but don't have the necessary disk space or the time to keep everything up to date. It's efficient: the installation and downloads are quicker, you only need to store one copy of each image however often it's used, containers need less RAM and share the same OS kernel, so you can have more servers running simultaneously, and it takes a matter of seconds to stop and start them, no need to wait for a full server boot.
### Example: Running your PHP Applications in Docker
After you [installed docker][docker-install] on your machine, you can start an Apache with PHP support in one step.
The following command will download a fully functional Apache installation with the latest PHP version and provide the
directory `/path/to/your/php/files` at `http://localhost:8080`:
After [installing docker][docker-install] on your machine, you can start a web server with one command.
The following will download a fully functional Apache installation with the latest PHP version, map `/path/to/your/php/files` to the document root, which you can view at `http://localhost:8080`:
{% highlight console %}
docker run -d --name my-php-webserver -p 8080:80 -v /path/to/your/php/files:/var/www/html/ php:apache
{% endhighlight %}
After running `docker run` your container is initialized and running.
If you would like to stop or start your container again, you can use the provided name attribute and simply run
`docker stop my-php-webserver` and `docker start my-php-webserver` without providing the above mentioned parameters
again.
This will initialize and launch your container. `-d` makes it runs in the background. To stop and start it, simply run `docker stop my-php-webserver` and `docker start my-php-webserver` (the other parameters are not needed again).
### Learn more about Docker
The commands mentioned above only show a quick way to run an Apache web server with PHP support but there are a lot
more things that you can do with Docker. One of the most important things for PHP developers will be linking your
web server to a database instance, for example. How this could be done is well described within the [Docker User Guide]
[docker-doc].
The command above shows a quick way to run a basic server. There's much more you can do (and thousands of pre-built images in the [Docker Hub][docker-hub]). Take time to learn the terminology and read the [Docker User Guide][docker-doc] to get the most from it, and don't run random code you've downloaded without checking it's safe unofficial images may not have the latest security patches. If in doubt, stick to the [official repositiories][docker-hub-official].
The [PHPDocker.io] site will auto-generate all the files you need for a fully-featured LAMP/LEMP stack, including your choice of PHP version and extensions.
* [Docker Website][Docker]
* [Docker Installation][docker-install]
* [Docker Images at the Docker Hub Registry][docker-hub]
* [Docker User Guide][docker-doc]
* [Docker Hub][docker-hub]
* [Docker Hub - official images][docker-hub-official]
[Docker]: http://docker.com/
[docker-hub]: https://hub.docker.com/
[docker-hub-official]: https://hub.docker.com/explore/
[docker-install]: https://docs.docker.com/installation/
[docker-doc]: https://docs.docker.com/userguide/
[PHPDocker.io]: https://phpdocker.io/generator

View File

@@ -17,6 +17,17 @@ PHP versions
* [PHP Best Practices](https://phpbestpractices.org/)
* [Best practices for Modern PHP Development](https://www.airpair.com/php/posts/best-practices-for-modern-php-development)
### News around the PHP and web development communities
You can subscribe to weekly newsletters to keep yourself informed on new libraries, latest news, events and general
announcements, as well as additional resources being published every now and then:
* [PHP Weekly](http://www.phpweekly.com)
* [JavaScript Weekly](http://javascriptweekly.com)
* [HTML5 Weekly](http://html5weekly.com)
* [Mobile Web Weekly](http://mobilewebweekly.co)
* There are also Weeklies on other platforms you might be interested in; here's
[a list of some](https://github.com/jondot/awesome-weekly).
### PHP universe
* [PHP Developer blog](http://blog.phpdeveloper.org/)

View File

@@ -6,7 +6,7 @@ title: Video Tutorials
## Video Tutorials {#videos}
### Youtube Channels
### YouTube Channels
* [PHP Academy](https://www.youtube.com/user/phpacademy)
* [The New Boston](https://www.youtube.com/user/thenewboston)
* [Sherif Ramadan](https://www.youtube.com/user/businessgeek)

View File

@@ -34,3 +34,4 @@ run from the command line.
* [The Grumpy Programmer's Guide To Building Testable PHP Applications](https://leanpub.com/grumpy-testing) - Learning
to write testable code doesn't have to suck.
* [Minimum Viable Tests](https://leanpub.com/minimumviabletests) - Long-time PHP testing evangelist Chris Hartjes goes over what he feels is the minimum you need to know to get started.
* [Domain-Driven Design in PHP](https://leanpub.com/ddd-in-php) - See real examples written in PHP showcasing Domain-Driven Design Architectural Styles (Hexagonal Architecture, CQRS or Event Sourcing), Tactical Design Patterns, and Bounded Context Integration.

View File

@@ -7,7 +7,7 @@ anchor: user_groups
If you live in a larger city, odds are there's a PHP user group nearby. You can easily find your local PUG at
the [usergroup-list at php.net][php-uglist] which is based upon [PHP.ug][php-ug]. Alternate sources might be
[Meetup.com][meetup] or a search for ```php user group near me``` using your favourite search engine
[Meetup.com][meetup] or a search for ```php user group near me``` using your favorite search engine
(i.e. [Google][google]). If you live in a smaller town, there may not be a local PUG; if that's the case, start one!
Special mention should be made of two global user groups: [NomadPHP] and [PHPWomen]. [NomadPHP] offers twice monthly

View File

@@ -139,6 +139,11 @@ pre{
pre{
padding: 5px 10px;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
}

View File

@@ -68,70 +68,7 @@ yourself a lot of trouble down the road by using factories.
When designing web applications, it often makes sense conceptually and architecturally to allow access to one and only
one instance of a particular class. The singleton pattern enables us to do this.
{% highlight php %}
<?php
class Singleton
{
/**
* @var Singleton The reference to *Singleton* instance of this class
*/
private static $instance;
/**
* Returns the *Singleton* instance of this class.
*
* @return Singleton The *Singleton* instance.
*/
public static function getInstance()
{
if (null === static::$instance) {
static::$instance = new static();
}
return static::$instance;
}
/**
* Protected constructor to prevent creating a new instance of the
* *Singleton* via the `new` operator from outside of this class.
*/
protected function __construct()
{
}
/**
* Private clone method to prevent cloning of the instance of the
* *Singleton* instance.
*
* @return void
*/
private function __clone()
{
}
/**
* Private unserialize method to prevent unserializing of the *Singleton*
* instance.
*
* @return void
*/
private function __wakeup()
{
}
}
class SingletonChild extends Singleton
{
}
$obj = Singleton::getInstance();
var_dump($obj === Singleton::getInstance()); // bool(true)
$anotherObj = SingletonChild::getInstance();
var_dump($anotherObj === Singleton::getInstance()); // bool(false)
var_dump($anotherObj === SingletonChild::getInstance()); // bool(true)
{% endhighlight %}
**TODO: NEED NEW SINGLETON CODE EXAMPLE**
The code above implements the singleton pattern using a [*static* variable](http://php.net/language.variables.scope#language.variables.scope.static) and the static creation method `getInstance()`.
Note the following:

View File

@@ -1,4 +1,4 @@
.highlight { background: #ffffff; }
.highlight { background: #ffffff; margin: 0 4px; font-size: 0.8em; }
.highlight .c { color: #999988; font-style: italic } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { font-weight: bold } /* Keyword */