This is for anyone who wants to learn to program in PHP as a second language.
+
You should already know at least one programming language with curly braces, if conditions, variables, functions and
+loops. The most likely scenario is that you are a Javascript or Typescript developer, working on a team that uses
+a combination of PHP and JS or TS, and while you might not currently need deep PHP knowledge,
+you want 'T-shaped skills', involving basic PHP knowledge. There will be some comparisons between with Javascript and
+Typescript.
+
Why did I write it?
+
(This part should probably be moved out of the intro, perhaps entirely out of the main tutorial content.)
+
I work as a PHP developer, and have some colleagues with great Javascript skills who sometimes want to do work in the PHP
+side of our application, but don't have PHP experience from their previous jobs. I looked around for an introductory
+tutorial to recommend they can work through, but I didn't find anything satisfying.
+
The tutorial at php.net appears not to have been updated for several years,
+and doesn't even introduce either the function or class keywords. On the other hand it does show PHP mixed with HTML,
+which I plan to pretend doesn't exist. I think it's now probably worse than useless.
+
David Brumbaugh's
+Learn a New Language: Migrating from JavaScript to PHP
+is much closer to what I want to write, but it's a bit too short. It doesn't explain how to install PHP, or what composer is,
+and again it prominently covers mixing PHP with HTML. It shows both the pre-and post 5.4 array syntax, whereas I want to
+require the reader to install at least PHP 7.2, and spend little if any time on older versions.
+
PHP The Right Way is often recommended to PHP beginners, for good reasons. It has very good
+advice, but it's not really a tutorial to work through step by step. I think it could be overwhelming as an introduction
+to PHP, and covers a lot more than I think most new PHP developers would want to know, including several paragraphs about
+the MySQL Extension, which as it says "is incredibly old and has been superseded by two other extensions". I plan to
+concentrate on the parts of PHP that I can recommend.
+
For PHP devs learning some Javascript, typescript, and/or React there seem to be great materials available online,
+such as the React Tutorial,
+HTML Dog's Javascript Tutorials,
+TypeScript in 5 minutes and the (paid) Typescript course at
+Execute Program. As far as I can tell there's nothing similar available free to help
+JS devs learn some PHP.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/02-getting-php.html b/02-getting-php.html
new file mode 100644
index 0000000..8c92916
--- /dev/null
+++ b/02-getting-php.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+ PHP as a Second Language
+
+
+
+
+
+
+
+
+
If you just want to run a PHP one-liner, or experiment with a tiny throwaway PHP script, the easiest way is probably
+online at 3v4l, but to work through this tutorial you should have PHP installed on your computer.
+
Installation
+
First check if you already have PHP installed. Open your command-line prompt, and type php --v. If you have PHP, you
+should see something like the following:
+
PHP 7.4.2 (cli) (built: Jan 23 2020 11:21:30) ( NTS )
+Copyright (c) The PHP Group
+Zend Engine v3.4.0, Copyright (c) Zend Technologies
+ with Zend OPcache v7.4.2, Copyright (c), by Zend Technologies
+
Check the first two parts of the version number, e.g. 7.4 above. This is the current version of PHP. Supported older
+versions are 7.3 and 7.2. If you have an older version you will need to upgrade before using this tutorial.
+
Very few people download PHP directly from php.net. If you work with PHP developers you may want to check where
+they install their PHP packages from and do the same. Installation methods vary with your operating system:
+
Linux
+
On Linux, use your package manager to install PHP, e.g. with the sudo apt install php command on Ubuntu, or Debian, and
+sudo dnf -y install php-cli on Fedora. However these may not give you a recent enough version of PHP. You can get
+more up to date packages for Ubuntu from Ondřej Surý for Ubuntu or
+from Remi's RPM Repository for Red Hat, CentOs and Fedora.
+
Macintosh
+
Apple supplies PHP as part of OS X, but this is 7.1, and you need at least 7.2. The best way to get that is probably
+through Homebrew. If you don't have it, first install the Homebrew package manager by following the instructions at
+brew.sh, and then enter the command brew install php && brew link php.
+
Windows
+
I personally don't have a Windows machine, but I think the easiest way to set up PHP on Windows is as part of the
+XAMPP package. Download and run the installer from Apache Friends. This
+package bundles PHP along with the Apache web server, MariaDB database server, and the perl language. Xampp is also available
+for Linux and Macintosh.
+
Run php -v again. Hopefully you will now have version 7.2 or later.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/03-hello-world.html b/03-hello-world.html
new file mode 100644
index 0000000..44b88a2
--- /dev/null
+++ b/03-hello-world.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+ PHP as a Second Language
+
+
+
+
+
+
+
+
+
At this point we're ready to start writing in PHP. Make a new folder for your PHP scripts.
+
If you have a favourite text editor or integrated development environment, such as vim, atom, or vs-code you may want to
+stick with that, but the best tool for editing PHP code is almost certainly JetBrains'
+PhpStorm. PhpStorm is free for 30 days, but will demand money after that.
+
Make a new file called hello.php, and type in the following:
Save the file, and go to your command line. Navigate to your PHP scripts folder, and run your first PHP program by
+typing php hello.php. You should see:
+
$ php hello.php
+Hello, world
+$
+
Every PHP file should start with <?php declare(strict_types=1); . This tells the PHP interpreter that you're writing
+PHP, and strict_types tells PHP not to try to guess what you mean so much. Historically PHP has tended to be very
+loose, and prefer to make the best guess of what's required if it isn't clear instead of giving up. That's probably good
+for writing a website one page at a time, but for applications we need the computer to stop and tell us what's wrong
+if we make a mistake, not plough ahead and give us a wrong result.
+
echo simply outputs strings of text. We could use print, which is quicker to say, but we prefer echo, which is
+20% shorter to write.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/04-http.html b/04-http.html
new file mode 100644
index 0000000..f981295
--- /dev/null
+++ b/04-http.html
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+ PHP as a Second Language
+
+
+
+
+
+
+
+
+
PHP is of course mostly used in web servers, so you might be wondering why we started with a command line script.
+
In fact you can access the same script through your web browser. PHP comes with a built-in web server for development
+and testing uses. To start it, run:
Go back to your command line and press ctrl-c to shut down the server.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/05-variables.html b/05-variables.html
new file mode 100644
index 0000000..ee8f6ed
--- /dev/null
+++ b/05-variables.html
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+ PHP as a Second Language
+
+
+
+
+
+
+
+
+
Variables in PHP always have a dollar sign $ at the start of their names, and you declare a variable by assigning a
+value to it. PHP interpolates double-quoted strings with variables. Edit hello.php to use a variable:
You can probably guess what this will do when you run it on your command line or serve it to your browser.
+
Arrays and loops
+
PHP has a very versatile built-in type called array. You're unlikely to find much PHP code that doesn't use arrays
+extensively, but they are easy to overuse at the cost of other more expressive types. Despite the name, a PHP array
+isn't really an array as you may know it from other languages. A PHP array is an ordered iterable dictionary, with keys
+and values. The keys may be strings or integers, and by default will be sequential integers starting at zero. The
+values can be anything that could go in a variable, including more arrays.
+
An array is not an object in PHP — we will get on to objects later.
+
Let's edit our script to declare an array and iterate through it:
foreach assigns each value from the array to the $planet variable in turn. We edit the script to ask it to give us
+the numeric keys that were implicitly set on line 3:
+
<?php declare(strict_types=1);
+
+$planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune'];
+
+foreach ($planets as $number => $planet) {
+ echo "Hello, $planet, you are planet number $number.\n";
+}
+
+$dwarfPlanet = 'Pluto';
+echo "Hello, $dwarfPlanet.\n";
Re-run the script - the planets are now listed in alphabetical order. But notice that the array keys have not changed -
+Mercury is still planet number 0, it's just that 0 now happens to come after 3. PHP array keys can come in any
+order.
+
Associative arrays
+
We can also assign array keys explicitly. When we're interested in the keys of an array we call it an
+associative array. For example:
+
+
\ No newline at end of file
diff --git a/06-functions.html b/06-functions.html
new file mode 100644
index 0000000..16cfa04
--- /dev/null
+++ b/06-functions.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+ PHP as a Second Language
+
+
+
+
+
+
+
+
+
PHP comes with hundreds of built in functions, to sort arrays, find the length of strings, match regexes, or even
+get the timestamp corresponding to midnight on Easter of a given year,
+you will inevitably want to define your own functions. Function definitions look like this:
+
function getGreeting(string $planetName): string
+{
+ return "Hello, $planetName.\n";
+}
+
Notice the type declarations, string and : string. Strictly speaking these are optional, but you should add
+them whenever you can. Like Typescript, PHP is a gradually typed language. However unlike Typescript, which does
+type checking in the compiler, PHP applies type checking only when the relevant part of your program runs. Let's see
+an example:
Hello, Magrathea.
+PHP Fatal error: Uncaught TypeError: Argument 1 passed to getGreeting() must be of the type string, int given, called in functions.php on line 9 and defined in functions.php:3
+Stack trace:
+#0 functions.php(9): getGreeting()
+#1 {main}
+ thrown in functions.php on line 3
+
The first part of the program ran fine, but attempting to pass an integer to a function that we've declared with a
+string parameter was a fatal error and caused a crash. If we'd tried to return anything other than a string from inside
+the function that would also be a fatal error.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/07-classes-1.html b/07-classes-1.html
new file mode 100644
index 0000000..f335c96
--- /dev/null
+++ b/07-classes-1.html
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+ PHP as a Second Language
+
+
+
+
+
+
+
+
+
PHP supports class-based object oriented programming, heavily influenced by Java and similar languages. This is
+different to the prototype based OO in JavaScript.
+
In the sort of PHP code I write almost 100% of the code is in a project is in classes - some classes are used to make
+objects, and others may just be convenient wrappers around groups of functions.
+
In PHP every object is an instance of a class, so you have to have a class before you can have an object. Let's write
+a class to let us make Planet objects. How to write classes changed a bit in PHP 7.4. Enter the
+following in a file called Planet.php:
+
<?php declare(strict_types=1);
+
+namespace PhpAsASecondLanguage;
+
+final class Planet
+{
+ private string $name;
+
+ private float $populationSize;
+
+ public function __construct(string $name, float $populationSize)
+ {
+ $this->name = $name;
+ $this->populationSize = $populationSize;
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function getPopulationSize(): float
+ {
+ return $this->populationSize;
+ }
+}
+
Older PHP Versions
+
name and populationSize are the properties of the class, and they have string and float types respectively.
+Before 7.4 PHP didn't allow us to specify types for properties. We still want to know what types of values we intend to
+put in the properties, so we use a DocBlock instead. If you don't have 7.4 yet, change the property declarations
+to:
These DocBlocks are ignored by the PHP engine, but they are very useful for us, and many tools and IDEs will read them.
+The code inside the docblock is written in the PHPDoc language.
+
Running the code
+
Run php Planet.php. You should see no output - a class by itself doesn't do anything. We will write code to use this
+class on the next page.
+
What's in the class
+
Let's read through the class from top to bottom.
+
+
+
The entire file is in a namespace. This is useful to help distinguish our code from other people's code, and
+to distinguish between submodules when our program gets bigger. We will keep all our code in this namespace. The
+namespace is effectively a prefix, so the full name of the class is \PhpAsASecondLanguage\Planet.
+
+
+
Planet is a final class. This prevents any other classes being written as subclasses of Planet. Subclassing is
+beyond the scope of this tutorial, but for now we can say that it adds significant complexity, and if we don't need it
+we should probably avoid it. It's therefore a good practice to make classes final by default - we can always delete the
+word final if we ever find we do need to make a subclasses of Planet.
+
+
+
Planet has name and populationSizeproperties. When we create Planet objects every object will have its own copy
+of these properties. private is the properties visibility - that means that the properties can only be directly read
+or written by code within the Planet class. In the PHP 7.4 code they are typed properties, which that PHP will
+do a type check at run time whenever we assign values to the properties, and it will only allow us to assign strings of
+text and and floating point numbers - attempting to assign anything else would a fatal error.
+
We would also get a fatal error if we tried to read these typed properties before they are initialised.
+
+
+
Next we have three functions, also known as methods. These all have public visibility, which means we can call them from anywhere.
+
Functions whose names start with __ are considered Magic Methods in PHP - they have special meanings given by the
+language. __construct is the Constructor, and will be automatically called whenever we create a Planet object with
+the new keyword.
+
Finally we have our two getter functions. Since the properties are private, these public functions are needed to allow
+code outside the class to read the properties. By keeping the properties private and creating getters but not setters
+we can create an immutable object. It's verbose, but it's a lot easier to understand what's happening in a big project
+with a class like this than it would be if the properties were public and code from lots of other places could be
+writing to them.
+
These functions will be run with a given object instance of the class. The $this variable will refer to that
+object. We use the arrow -> operator to access the properties and methods of any object.
+
Having getters also means that if we later want to change the class - perhaps to replace the
+$populationSize property with an array that holds details of every person on the planet - we can edit the code inside
+the getter function and make it return the size of the array. Code that uses this class wouldn't have to be
+affected by the change. Changes tend to ripple through a codebase, and one of the most important things to do when
+choosing how to write code is anticipating types of future changes, and putting barriers in place to limit the spread of
+those changes.
+
+
+
If you know JavaScript, you can think of a class with public and private parts as serving a similar purpose to a
+JavaScript module that exports some but not all of its symbols.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/08-classes-2.html b/08-classes-2.html
new file mode 100644
index 0000000..95b9d99
--- /dev/null
+++ b/08-classes-2.html
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+ PHP as a Second Language
+
+
+
+
+
+
+
+
+
On the last page we wrote Planet class, and saved it in a file called Planet.php. Now we want to put that class
+to work.
+
PHP works by executing whatever script we give it from beginning to end. It won't run anything inside a class unless
+we that script tells it to, so every PHP program needs at least one line that isn't part of any class as an entry-point.
+There is no equivalent to the main function of languages like C and Java.
+
By convention, and to follow the PSR-1 coding standard, we always put the entry point in a separate file to the
+class declaration.
+
Let's write a simple script to create a planet and print out its details. Since at the moment Planet is just a data
+holder the script won't be too exiting.
+
Write the following in a file called start.php
+
<?php declare(strict_types=1);
+
+namespace PhpAsASecondLanguage;
+
+$planet = new Planet('Neptune', 0);
+
+echo "Planet {$planet->getName()} has a population of {$planet->getPopulationSize()}.\n";
+
The new keyword creates objects instances from classes, and automatically runs any constructor with the arguments we pass.
+
We can try running this now but it won't work just yet, because we need to link it up with Planet.php. When you type
+php start.php you should see PHP Fatal error: Uncaught Error: Class 'Planet' not found.
+
Linking files together
+
There are two main ways to link files together. The old way is require_once, and the new ways is composer. We'll
+start with require_once, but for all but the simplest of scripts we will want to use composer.
+
Require_once
+
Edit start.php to make it look like this:
+
<?php declare(strict_types=1);
+
+namespace PhpAsASecondLanguage;
+
+require_once __DIR__ . '/Planet.php';
+
+$planet = new Planet('Neptune', 0);
+
+echo "Planet {$planet->getName()} has a population of {$planet->getPopulationSize()}.\n";
+
Run the script again, and you should now see Planet Neptune has a population of 0.. The require_once statement
+tells PHP to process the contents of the given file as if it had been pasted in to the current script, ignoring
+the <?php opening tag and the declare. The once part means that if we require the same file more than once PHP will
+skip it on the second and subsequent times. That's what want for a class - once the class is loaded there's no need to
+load it again, even if multiple parts of our program have to declare that they need that class.
+
__DIR__ is a PHP magic constant that refers to the directory of whatever file its used in. The dot . is PHP's string
+concatenation operator.
+
But adding a require_once statement every time we need to use a class can quickly become tedious. It's what we all
+mostly did until around 2015, when the Composer dependency management tool became popular, even prompting a rare
+mention for PHP in the ThoughtWorks Technology Radar.
+
Composer
+
Composer is primarily a tool to help you add third party libraries and frameworks to your PHP projects, but since it
+includes code to help us load the classes those libraries declare it makes sense to also use it to help us load our own
+classes.
+
First check if you have composer installed - run composer about. On some systems it may be called composer.phar
+instead of composer. If you have it, you should see "Composer - Dependency Manager for PHP". If not,
+download and install composer from getcomposer.org, then come back to this page. I
+suggest installing it to somewhere on your executable PATH, and naming it composer rather than composer.phar.
+
Let's adjust our script to use composer instead of require_once. First delete the require_once statement, to get back
+to the class not found error we had before.
+
Create a subdirectory src and move Planet.php inside src. It's generally a good idea to have this to keep the bulk
+of our source code separate from everything else in our project, e.g. the entry point file, any docs we might want to write,
+tool config etc.
+
Composer works on a project-by-project basis. To set it up for your a project, you need to create a composer.json file.
+To start with, just put an empty json object in this file:
+
{
+}
+
You can now run composer install. Composer should create a vendor subdirectory. Everything you install to you project
+through composer will be in that directory. You would normally want to exclude vendor from source control.
+
Now we need to tell composer how to load your classes. The PSR-4 scheme is a standard way of translating between PHP
+class names and file paths. Edit composer.json to look like the following:
Re-run composer install, and then edit start.php to require Composer's autoloader:
+
<?php declare(strict_types=1);
+
+namespace PhpAsASecondLanguage;
+
+require_once __DIR__. '/vendor/autoload.php';
+
+$planet = new Planet('Neptune', 0);
+echo "Planet {$planet->getName()} has a population of {$planet->getPopulationSize()}.\n";
+
+
As long as we keep our file names matching our class names, Composer will automatically load any class we need, and
+only when we need it.
+
If you run php start.php you should once again learn the population of Neptune.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/09-classes-3.html b/09-classes-3.html
new file mode 100644
index 0000000..2bfc17b
--- /dev/null
+++ b/09-classes-3.html
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+ PHP as a Second Language
+
+
+
+
+
+
+
+
+
Not all the methods on a class have to run in the context of an object. Methods that work without a $this object
+instance are called static methods. Let's add a static method to the Planet class in src/Planet.php:
+
<?php declare(strict_types=1);
+
+namespace PhpAsASecondLanguage;
+
+final class Planet
+{
+ private string $name;
+
+ private float $populationSize;
+
+ public function __construct(string $name, float $populationSize)
+ {
+ $this->name = $name;
+ $this->populationSize = $populationSize;
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function getPopulationSize(): float
+ {
+ return $this->populationSize;
+ }
+
+ public static function earth(): self
+ {
+ return new self('Earth', 7.7 * 10**9);
+ }
+}
+
+
The self keyword refers to whatever class it's written in. It's more convenient to write self than to repeat
+Planet many times.
+
And let's edit start.php:
+
<?php declare(strict_types=1);
+
+namespace PhpAsASecondLanguage;
+
+require_once __DIR__. '/vendor/autoload.php';
+
+$planet = Planet::Earth();
+echo "Planet {$planet->getName()} has a population of {$planet->getPopulationSize()}.\n";
+
The double colon is officially called the Scope Resolution Operator. It access properties and methods of classes
+without going through an object. In this case our method returns an instance of the class, but it could
+do anything.
+
Object Identity
+
A PHP variable can't actually hold an object - instead it holds an object identifier, also known as a reference.
+
To objects created the same way, with the same properties will have distinct identities. But if one object is created
+and then its assigned to two variables, they will both refer hold identifiers for the same object. This becomes
+important when we make our objects mutable. This extra complexity is a good reason to prefer immutable objects, but
+we will need mutability at times.
+
Let's add a function to change the world - edit src/Planet.php again, adding the following function inside the class:
+
public function receiveImmigrant(): void
+ {
+ $this->populationSize++;
+ }
+
void is the return type for functions that don't actually return any information.
+
Let's write a script that illustrates identities. Enter the following in 'identities.php'
+
<?php declare(strict_types=1);
+
+namespace PhpAsASecondLanguage;
+
+require_once __DIR__. '/vendor/autoload.php';
+
+$mercury = new Planet('Mercury', 0);
+$secondMercury = new Planet('Mercury', 0);
+$theSameMercury = $mercury;
+
+echo "Mercury is equal to second Mercury:\n";
+var_dump($mercury == $secondMercury);
+echo \PHP_EOL;
+
+echo "But Mercury is not **identical** to second Mercury:\n";
+var_dump($mercury === $secondMercury);
+echo \PHP_EOL;
+
+echo "Mercury is equal **and** identical to the same Mercury:\n";
+var_dump($mercury == $theSameMercury);
+var_dump($mercury === $theSameMercury);
+echo \PHP_EOL;
+
+$mercury->receiveImmigrant();
+
+echo "Population of Mercury: {$mercury->getPopulationSize()}.\n";
+echo "Population of second Mercury: {$secondMercury->getPopulationSize()}.\n";
+echo "Population of the same Mercury: {$theSameMercury->getPopulationSize()}.\n";
+
We see that $mercury and $theSameMercury are just two names for the same object, while $secondMercury is an
+entirely separate object with its own properties, lifecycle, hopes and dreams.
+
If we use or write a function that accepts an object as a parameter, or returns an object, it's important to be aware
+that PHP doesn't make a copy of the object - it just copies the identifier. This means that code within and without
+the function can access and potentially change the same object. It's an important part of how communication happens
+between the parts of a PHP program, but it can easily get confusing if not managed carefully.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/10-testing.html b/10-testing.html
new file mode 100644
index 0000000..b5e449a
--- /dev/null
+++ b/10-testing.html
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+ PHP as a Second Language
+
+
+
+
+
+
+
+
+
If we want to work on even a moderately complex program over time, we need automated testing - manually testing
+everything every time we make a change would quickly become unsustainable.
+
Installing PHPUnit
+
The leading test framework for PHP is Sebastian Bergmann's PHPUnit.
+
Since we already have composer set up for our project, we can use that to install PHPUnit in the vendor directory. Run:
+
composer require --dev phpunit/phpunit
+
Composer also automatically downloads and installs all the libraries that PHPUnit depends on, and the dependencies of
+its dependencies, etc.
+
We use the --dev option because PHPUnit is a tool for developers, and not a library that our program would rely on
+in production. If we wanted to prepare a copy of our program to install on a server, we would use the --no-dev option
+of composer install to leave out PHPUnit.
+
When we ran the require command composer.json edited our composer.json file to record our updated requirements.
+composer.json should now look like:
The last major release of PHPUnit was 9.0, so composer has assumed we will always want whatever the latest PHPUnit
+release in the 9 series is. The 10 series is not expected to be compatible with code written for PHPUnit 9, so composer
+won't install that unless we edit composer json. Composer works best with dependencies that use semantic versioning,
+aka semver.
+
Composer has also created a new file for us, composer.lock. This has metadata about the exact versions of the packages
+installed. At the time of writing it shows me that PHPUnit is at version 9.0.1, and I can see the details of 29 other
+packages that have been installed because PHPUnit depends on them directly or indirectly. The composer show command
+will output the list of installed packages in a much more consice format.
+
Writing a test
+
Let's write our first test. Create a test subdirectory next to src, and write the following in test/PlanetTest.php
+
<?php declare(strict_types=1);
+
+namespace PhpAsASecondLanguage;
+
+use PHPUnit\Framework\TestCase;
+
+final class PlanetTest extends TestCase
+{
+ private Planet $SUT; // SUT = Subject Under Test
+
+ public function setUp(): void
+ {
+ $this->SUT = new Planet('planet name', 0);
+ }
+
+ public function test_it_can_accept_immigrant(): void
+ {
+ $this->assertSame(0.0, $this->SUT->getPopulationSize());
+ $this->SUT->receiveImmigrant();
+ $this->assertSame(1.0, $this->SUT->getPopulationSize());
+ }
+}
+
The new keyword here is extends. This means that our class is an extension, or subclass of PHPUnit's TestCase class,
+which is how PHPUnit is designed to be used. If TestCase has been marked final we wouldn't be able to extend it.
+
If you're not on PHP 7.4, remember to remove the Planet property type for the SUT, and replace it with a docbloc as we
+did for the properties of Planet itself.
+
It's prudent to see a test fail at least once before believing what it says when it passes. To make it fail, comment out
+$this->populationSize++; in src/Planet.php:
+
// $this->populationSize++;
+
Now run the PHPUnit command:
+
vendor/bin/phpunit test
+
This will search for any filenames ending in Test.php in the test directory. In each test case any public function
+whose name starts with test is considered a test. For every test PHPUnit creates an instance of the class, calls the
+setup function, then calls the test function, records the results, and then throws away the object. So if we had two test
+it would run the set up function twice. If an object is referenced only by garbage it is garbage, so when the test case
+object is thrown away the Planet is thrown away too, and any mutations to the planet will not affect the next test.
+
The output from PHPUnit should look like:
+
PHPUnit 9.0.1 by Sebastian Bergmann and contributors.
+
+F 1 / 1 (100%)
+
+Time: 36 ms, Memory: 4.00 MB
+
+There was 1 failure:
+
+1) PhpAsASecondLanguage\PlanetTest::test_it_can_accept_immigrant
+Failed asserting that 0.0 is identical to 1.0.
+
+/tmp/composerPlayground/test/PlanetTest.php:20
+
+FAILURES!
+Tests: 1, Assertions: 2, Failures: 1.
+
Fix the Planet class putting the increment back in, and re-run the PHPUnit command. We should now see some happier
+output:
+
PHPUnit 9.0.1 by Sebastian Bergmann and contributors.
+
+. 1 / 1 (100%)
+
+Time: 35 ms, Memory: 4.00 MB
+
+OK (1 test, 2 assertions)
+
Writing tests is a huge topic, which we can't cover in detail here. PHPUnit has excellent
+official documentation. You might choose to do
+Test Driven Development (TDD) / Behaviour Driven Development (BDD) and write your tests before writing the
+production code that they cover.
+
Some other major test frameworks for PHP are PHPSpec and
+Behat, which are both designed around the BDD approach, which
+uses the language of executable specifications rather than tests. The major difference is that in PHPSpec, as with
+PHPUnit, you code in PHP. In Behat you code in a separate language called gherkin, designed to be readable by project
+stakeholders who haven't been trained in programming.
This is for anyone who wants to learn to program in PHP as a second language.
+
You should already know at least one programming language with curly braces, if conditions, variables, functions and
+loops. The most likely scenario is that you are a Javascript or Typescript developer, working on a team that uses
+a combination of PHP and JS or TS, and while you might not currently need deep PHP knowledge,
+you want 'T-shaped skills', involving basic PHP knowledge. There will be some comparisons between with Javascript and
+Typescript.
+
Why did I write it?
+
(This part should probably be moved out of the intro, perhaps entirely out of the main tutorial content.)
+
I work as a PHP developer, and have some colleagues with great Javascript skills who sometimes want to do work in the PHP
+side of our application, but don't have PHP experience from their previous jobs. I looked around for an introductory
+tutorial to recommend they can work through, but I didn't find anything satisfying.
+
The tutorial at php.net appears not to have been updated for several years,
+and doesn't even introduce either the function or class keywords. On the other hand it does show PHP mixed with HTML,
+which I plan to pretend doesn't exist. I think it's now probably worse than useless.
+
David Brumbaugh's
+Learn a New Language: Migrating from JavaScript to PHP
+is much closer to what I want to write, but it's a bit too short. It doesn't explain how to install PHP, or what composer is,
+and again it prominently covers mixing PHP with HTML. It shows both the pre-and post 5.4 array syntax, whereas I want to
+require the reader to install at least PHP 7.2, and spend little if any time on older versions.
+
PHP The Right Way is often recommended to PHP beginners, for good reasons. It has very good
+advice, but it's not really a tutorial to work through step by step. I think it could be overwhelming as an introduction
+to PHP, and covers a lot more than I think most new PHP developers would want to know, including several paragraphs about
+the MySQL Extension, which as it says "is incredibly old and has been superseded by two other extensions". I plan to
+concentrate on the parts of PHP that I can recommend.
+
For PHP devs learning some Javascript, typescript, and/or React there seem to be great materials available online,
+such as the React Tutorial,
+HTML Dog's Javascript Tutorials,
+TypeScript in 5 minutes and the (paid) Typescript course at
+Execute Program. As far as I can tell there's nothing similar available free to help
+JS devs learn some PHP.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
From 0cba002b03c2978780ff75659b7ecdd235438c9a Mon Sep 17 00:00:00 2001
From: Circle CI
Date: Tue, 25 Feb 2020 20:25:35 +0000
Subject: [PATCH 06/40] Update GH Pages based on CircleCI Build 76
---
01-introduction.html | 4 ++--
02-getting-php.html | 4 ++--
03-hello-world.html | 4 ++--
04-http.html | 4 ++--
05-variables.html | 4 ++--
06-functions.html | 4 ++--
07-classes-1.html | 4 ++--
08-classes-2.html | 4 ++--
09-classes-3.html | 4 ++--
10-testing.html | 4 ++--
index.html | 4 ++--
11 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/01-introduction.html b/01-introduction.html
index 1813ca0..b70aa76 100644
--- a/01-introduction.html
+++ b/01-introduction.html
@@ -16,7 +16,7 @@
(This part should probably be moved out of the intro, perhaps entirely out of the main tutorial content.)
I work as a PHP developer, and have some colleagues with great Javascript skills who sometimes want to do work in the PHP
side of our application, but don't have PHP experience from their previous jobs. I looked around for an introductory
-tutorial to recommend they can work through, but I didn't find anything satisfying.
+tutorial to recommend they can work through, but I didn't find anything satisfying.
The tutorial at php.net appears not to have been updated for several years,
-and doesn't even introduce either the function or class keywords. On the other hand it does show PHP mixed with HTML,
+and doesn't even introduce the function or class keywords. On the other hand it does show PHP mixed with HTML,
which I plan to pretend doesn't exist. I think it's now probably worse than useless.
David Brumbaugh's
Learn a New Language: Migrating from JavaScript to PHP
-is much closer to what I want to write, but it's a bit too short. It doesn't explain how to install PHP, or what composer is,
+is much closer to what I want to write, but it's a bit too short. It doesn't explain how to install PHP, or what Composer is,
and again it prominently covers mixing PHP with HTML. It shows both the pre-and post 5.4 array syntax, whereas I want to
require the reader to install at least PHP 7.2, and spend little if any time on older versions.
PHP The Right Way is often recommended to PHP beginners, for good reasons. It has very good
@@ -94,7 +94,7 @@
online at 3v4l, but to work through this tutorial you should have PHP installed on your computer.
Installation
First check if you already have PHP installed. Open your command-line prompt, and type php --v. If you have PHP, you
-should see something like the following:
+should see something like:
PHP 7.4.2 (cli) (built: Jan 23 2020 11:21:30) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.2, Copyright (c), by Zend Technologies
-
Check the first two parts of the version number, e.g. 7.4 above. This is the current version of PHP. Supported older
-versions are 7.3 and 7.2. If you have an older version you will need to upgrade before using this tutorial.
+
Check the version number - ideally it should be 7.4.x but at minimum to follow this tutorial it needs to be at least
+7.2.
Very few people download PHP directly from php.net. If you work with PHP developers you may want to check where
they install their PHP packages from and do the same. Installation methods vary with your operating system:
Linux
-
On Linux, use your package manager to install PHP, e.g. with the sudo apt install php command on Ubuntu, or Debian, and
+
On Linux, use your package manager to install PHP, e.g. with the sudo apt install php command on Ubuntu or Debian, and
sudo dnf -y install php-cli on Fedora. However these may not give you a recent enough version of PHP. You can get
more up to date packages for Ubuntu from Ondřej Surý for Ubuntu or
from Remi's RPM Repository for Red Hat, CentOs and Fedora.
Macintosh
Apple supplies PHP as part of OS X, but this is 7.1, and you need at least 7.2. The best way to get that is probably
through Homebrew. If you don't have it, first install the Homebrew package manager by following the instructions at
-brew.sh, and then enter the command brew install php && brew link php.
+brew.sh, and then enter the command brew install php && brew link php.
Windows
-
I personally don't have a Windows machine, but I think the easiest way to set up PHP on Windows is as part of the
+
I personally don't have a Windows machine, but I think the easiest way to set up PHP on Windows may be as part of the
XAMPP package. Download and run the installer from Apache Friends. This
package bundles PHP along with the Apache web server, MariaDB database server, and the perl language. Xampp is also available
-for Linux and Macintosh.
+for Linux and Macintosh.
Run php -v again. Hopefully you will now have version 7.2 or later.
At this point we're ready to start writing in PHP. Make a new folder for your PHP scripts.
-
If you have a favourite text editor or integrated development environment, such as vim, atom, or vs-code you may want to
+
At this point we're ready to start writing in PHP. Make a new folder for your PHP code.
+
If you have a favourite text editor or integrated development environment, such as Vim, Atom, or VS Code you may want to
stick with that, but the best tool for editing PHP code is almost certainly JetBrains'
PhpStorm. PhpStorm is free for 30 days, but will demand money after that.
-
Make a new file called hello.php, and type in the following:
+
Make a new file called hello.php with your editor or IDE of choice, and type the following:
Save the file, and go to your command line. Navigate to your PHP scripts folder, and run your first PHP program by
+
Save the file, and go to your command line. Navigate to your PHP code folder, and run it by
typing php hello.php. You should see:
$ php hello.php
Hello, world
$
-
Every PHP file should start with <?php declare(strict_types=1); . This tells the PHP interpreter that you're writing
+
Every PHP file should start with <?php declare(strict_types=1);. <?php tells the PHP interpreter that you're writing
PHP, and strict_types tells PHP not to try to guess what you mean so much. Historically PHP has tended to be very
loose, and prefer to make the best guess of what's required if it isn't clear instead of giving up. That's probably good
for writing a website one page at a time, but for applications we need the computer to stop and tell us what's wrong
@@ -84,7 +84,7 @@
Go back to your command line and press ctrl-c to shut down the server.
+
This tutorial treats PHP as a general purpose programming language - we won't focus on the specifics of running in a web
+server. In a big web application only a small part of the code may need to deal with getting data in and out through
+HTTP, and usually this will be abstracted away to some extent by a framework like Symfony or Laravel, or a CMS like
+Wordpress or Drupal.
PHP has a very versatile built-in type called array. You're unlikely to find much PHP code that doesn't use arrays
extensively, but they are easy to overuse at the cost of other more expressive types. Despite the name, a PHP array
-isn't really an array as you may know it from other languages. A PHP array is an ordered iterable dictionary, with keys
+isn't really an array as you may know it from other languages. A PHP array is an ordered iterable map, with keys
and values. The keys may be strings or integers, and by default will be sequential integers starting at zero. The
values can be anything that could go in a variable, including more arrays.
-
An array is not an object in PHP — we will get on to objects later.
+
An array is not an object in PHP. We will cover objects later.
Let's edit our script to declare an array and iterate through it:
foreach assigns each value from the array to the $planet variable in turn. We edit the script to ask it to give us
-the numeric keys that were implicitly set on line 3:
+
foreach assigns each value from the array to the $planet variable in turn. We edit the script to print keys as well
+as values:
Re-run the script - the planets are now listed in alphabetical order. But notice that the array keys have not changed -
-Mercury is still planet number 0, it's just that 0 now happens to come after 3. PHP array keys can come in any
+try one: array_reverse. Add new line before foreach:
+
$planets = array_reverse($planets, true);
+
Re-run the script - the planets are now listed in reverse order. But notice that the array keys have not changed -
+Mercury is still planet number 0, it's just that 0 now comes last. PHP array keys can come in any
order.
Associative arrays
We can also assign array keys explicitly. When we're interested in the keys of an array we call it an
@@ -138,7 +138,7 @@
PHP comes with hundreds of built in functions, to sort arrays, find the length of strings, match regexes, or even
-get the timestamp corresponding to midnight on Easter of a given year,
-you will inevitably want to define your own functions. Function definitions look like this:
function getGreeting(string $planetName): string
{
return "Hello, $planetName.\n";
}
Notice the type declarations, string and : string. Strictly speaking these are optional, but you should add
-them whenever you can. Like Typescript, PHP is a gradually typed language. However unlike Typescript, which does
+them whenever you can. PHP is a gradually typed language, like TypeScript. However unlike Typescript, which does
type checking in the compiler, PHP applies type checking only when the relevant part of your program runs. Let's see
an example:
PHP supports class-based object oriented programming, heavily influenced by Java and similar languages. This is
different to the prototype based OO in JavaScript.
-
In the sort of PHP code I write almost 100% of the code is in a project is in classes - some classes are used to make
+
In the sort of PHP code I write almost 100% of the code in a project is in classes - some classes are used to make
objects, and others may just be convenient wrappers around groups of functions.
In PHP every object is an instance of a class, so you have to have a class before you can have an object. Let's write
a class to let us make Planet objects. How to write classes changed a bit in PHP 7.4. Enter the
@@ -66,7 +66,7 @@
Classes and Objects
private float $populationSize;
- public function __construct(string $name, float $populationSize)
+ public function __construct(string $name, float $populationSize)
{
$this->name = $name;
$this->populationSize = $populationSize;
@@ -95,9 +95,10 @@
These DocBlocks are ignored by the PHP engine, but they are very useful for us, and many tools and IDEs will read them.
-The code inside the docblock is written in the PHPDoc language.
+The code inside the docblock is written in the PHPDoc
+language.
Running the code
Run php Planet.php. You should see no output - a class by itself doesn't do anything. We will write code to use this
class on the next page.
@@ -113,28 +114,29 @@
What's in the class
Planet is a final class. This prevents any other classes being written as subclasses of Planet. Subclassing is
beyond the scope of this tutorial, but for now we can say that it adds significant complexity, and if we don't need it
we should probably avoid it. It's therefore a good practice to make classes final by default - we can always delete the
-word final if we ever find we do need to make a subclasses of Planet.
+word final if we ever find we do need to make a subclasses of Planet.
Planet has name and populationSizeproperties. When we create Planet objects every object will have its own copy
-of these properties. private is the properties visibility - that means that the properties can only be directly read
-or written by code within the Planet class. In the PHP 7.4 code they are typed properties, which that PHP will
+of these properties. The properties have privatevisibility - that means that the properties can only be directly read
+or written by code within the Planet class.
+
In the PHP 7.4 code they are typed properties, so PHP will
do a type check at run time whenever we assign values to the properties, and it will only allow us to assign strings of
-text and and floating point numbers - attempting to assign anything else would a fatal error.
-
We would also get a fatal error if we tried to read these typed properties before they are initialised.
+text and and floating point numbers - attempting to assign anything else would cause a fatal error.
+
We would also get a fatal error if we tried to read these typed properties before assigning values to them.
-
Next we have three functions, also known as methods. These all have public visibility, which means we can call them from anywhere.
+
Next we have three class functions, also known as methods. These all have public visibility, which means we can
+call them from anywhere.
Functions whose names start with __ are considered Magic Methods in PHP - they have special meanings given by the
language. __construct is the Constructor, and will be automatically called whenever we create a Planet object with
the new keyword.
Finally we have our two getter functions. Since the properties are private, these public functions are needed to allow
-code outside the class to read the properties. By keeping the properties private and creating getters but not setters
-we can create an immutable object. It's verbose, but it's a lot easier to understand what's happening in a big project
+code outside the class to read the properties. It's verbose, but it's a lot easier to understand what's happening in a big project
with a class like this than it would be if the properties were public and code from lots of other places could be
writing to them.
These functions will be run with a given object instance of the class. The $this variable will refer to that
-object. We use the arrow -> operator to access the properties and methods of any object.
+object. We use the arrow -> operator to access the properties and methods of any object.
Having getters also means that if we later want to change the class - perhaps to replace the
$populationSize property with an array that holds details of every person on the planet - we can edit the code inside
the getter function and make it return the size of the array. Code that uses this class wouldn't have to be
@@ -160,7 +162,7 @@
PHP works by executing whatever script we give it from beginning to end. It won't run anything inside a class unless
we that script tells it to, so every PHP program needs at least one line that isn't part of any class as an entry-point.
There is no equivalent to the main function of languages like C and Java.
-
By convention, and to follow the PSR-1 coding standard, we always put the entry point in a separate file to the
-class declaration.
+
By convention, and to follow the PSR-1 coding standard, we always put the entry
+point in a separate file to the class declaration.
Let's write a simple script to create a planet and print out its details. Since at the moment Planet is just a data
holder the script won't be too exiting.
Write the following in a file called start.php
@@ -66,12 +66,12 @@
Classes Part 2
$planet = new Planet('Neptune', 0);
echo "Planet {$planet->getName()} has a population of {$planet->getPopulationSize()}.\n";
-
The new keyword creates objects instances from classes, and automatically runs any constructor with the arguments we pass.
+
The new keyword creates objects instances from classes, and automatically calls any constructor the class has.
We can try running this now but it won't work just yet, because we need to link it up with Planet.php. When you type
php start.php you should see PHP Fatal error: Uncaught Error: Class 'Planet' not found.
Linking files together
There are two main ways to link files together. The old way is require_once, and the new ways is composer. We'll
-start with require_once, but for all but the simplest of scripts we will want to use composer.
echo "Planet {$planet->getName()} has a population of {$planet->getPopulationSize()}.\n";
Run the script again, and you should now see Planet Neptune has a population of 0.. The require_once statement
tells PHP to process the contents of the given file as if it had been pasted in to the current script, ignoring
-the <?php opening tag and the declare. The once part means that if we require the same file more than once PHP will
-skip it on the second and subsequent times. That's what want for a class - once the class is loaded there's no need to
-load it again, even if multiple parts of our program have to declare that they need that class.
+the <?php opening tag and the declare. The once part means that if we require_once the same file more than once
+PHP will skip it on the second and subsequent times. That's what want for a class - once the class is loaded there's no
+need to load it again, even if multiple parts of our program have to declare that they need that class.
__DIR__ is a PHP magic constant that refers to the directory of whatever file its used in. The dot . is PHP's string
concatenation operator.
But adding a require_once statement every time we need to use a class can quickly become tedious. It's what we all
-mostly did until around 2015, when the Composer dependency management tool became popular, even prompting a rare
-mention for PHP in the ThoughtWorks Technology Radar.
+mostly did until around 2015, when the Composer dependency management tool became popular, even prompting a rare
+mention for PHP in the ThoughtWorks Technology Radar. Nowadays we
+almost always want to use Composer.
Composer
Composer is primarily a tool to help you add third party libraries and frameworks to your PHP projects, but since it
includes code to help us load the classes those libraries declare it makes sense to also use it to help us load our own
@@ -105,7 +106,7 @@
Composer
to the class not found error we had before.
Create a subdirectory src and move Planet.php inside src. It's generally a good idea to have this to keep the bulk
of our source code separate from everything else in our project, e.g. the entry point file, any docs we might want to write,
-tool config etc.
+tool config etc.
Composer works on a project-by-project basis. To set it up for your a project, you need to create a composer.json file.
To start with, just put an empty json object in this file:
{
@@ -133,6 +134,8 @@
Composer
As long as we keep our file names matching our class names, Composer will automatically load any class we need, and
only when we need it.
+
Of course technically we haven't got rid of the require_once statement, but by requiring the autoloader we won't
+need to add any more require statements, however classes we use in our program.
If you run php start.php you should once again learn the population of Neptune.
private float $populationSize;
- public function __construct(string $name, float $populationSize)
+ public function __construct(string $name, float $populationSize)
{
$this->name = $name;
$this->populationSize = $populationSize;
@@ -83,9 +83,9 @@
Static methods
return new self('Earth', 7.7 * 10**9);
}
}
-
The self keyword refers to whatever class it's written in. It's more convenient to write self than to repeat
Planet many times.
@@ -98,22 +98,21 @@
Static methods
$planet = Planet::Earth();
echo "Planet {$planet->getName()} has a population of {$planet->getPopulationSize()}.\n";
-
The double colon is officially called the Scope Resolution Operator. It access properties and methods of classes
-without going through an object. In this case our method returns an instance of the class, but it could
-do anything.
+
The double colon is the Scope Resolution Operator. It accesses static methods (and properties) of classes - there is
+no need to have an object first. In this case our method returns an instance of the class, but it could do anything.
Object Identity
A PHP variable can't actually hold an object - instead it holds an object identifier, also known as a reference.
-
To objects created the same way, with the same properties will have distinct identities. But if one object is created
-and then its assigned to two variables, they will both refer hold identifiers for the same object. This becomes
-important when we make our objects mutable. This extra complexity is a good reason to prefer immutable objects, but
-we will need mutability at times.
+
Two objects created the same way, with the same properties will have distinct identities. But if one object is created
+and then assigned to two variables, they will both hold identifiers for the same object. This becomes important when we
+make our objects mutable. This extra complexity is a good reason to prefer immutable objects, but sometimes we need
+mutability.
Let's add a function to change the world - edit src/Planet.php again, adding the following function inside the class:
public function receiveImmigrant(): void
{
$this->populationSize++;
}
void is the return type for functions that don't actually return any information.
-
Let's write a script that illustrates identities. Enter the following in 'identities.php'
+
Now let's write a script to illustrate object identities. Enter the following in 'identities.php'
echo "Population of the same Mercury: {$theSameMercury->getPopulationSize()}.\n";
We see that $mercury and $theSameMercury are just two names for the same object, while $secondMercury is an
entirely separate object with its own properties, lifecycle, hopes and dreams.
-
If we use or write a function that accepts an object as a parameter, or returns an object, it's important to be aware
-that PHP doesn't make a copy of the object - it just copies the identifier. This means that code within and without
-the function can access and potentially change the same object. It's an important part of how communication happens
-between the parts of a PHP program, but it can easily get confusing if not managed carefully.
+
If a function accepts an object as a parameter, or returns an object, PHP doesn't make a copy of the object - it just
+copies the identifier. This means that code within and without the function can access and potentially change the same
+object. It's an important part of how communication happens between the parts of a PHP program, but it can easily get
+confusing if not managed carefully.
Composer also automatically downloads and installs all the libraries that PHPUnit depends on, and the dependencies of
its dependencies, etc.
We use the --dev option because PHPUnit is a tool for developers, and not a library that our program would rely on
-in production. If we wanted to prepare a copy of our program to install on a server, we would use the --no-dev option
-of composer install to leave out PHPUnit.
-
When we ran the require command composer.json edited our composer.json file to record our updated requirements.
+in production. If we wanted to prepare a copy of our program to install on a server, we would use
+composer install --no-dev install any libraries we need and set up the autoloader but leave out PHPUnit.
+
When we ran the require command composer.json edited our composer.json file to record our updated requirements.
composer.json should now look like:
{
"autoload": {
@@ -74,14 +74,13 @@
Installing PHPUnit
}
The last major release of PHPUnit was 9.0, so composer has assumed we will always want whatever the latest PHPUnit
release in the 9 series is. The 10 series is not expected to be compatible with code written for PHPUnit 9, so composer
-won't install that unless we edit composer json. Composer works best with dependencies that use semantic versioning,
-aka semver.
+won't install that unless we edit composer json. Composer works best with dependencies that use semantic versioning.
Composer has also created a new file for us, composer.lock. This has metadata about the exact versions of the packages
installed. At the time of writing it shows me that PHPUnit is at version 9.0.1, and I can see the details of 29 other
packages that have been installed because PHPUnit depends on them directly or indirectly. The composer show command
will output the list of installed packages in a much more consice format.
Writing a test
-
Let's write our first test. Create a test subdirectory next to src, and write the following in test/PlanetTest.php
+
Let's write our first test. Create a test subdirectory next to src, and write the following in test/PlanetTest.php
The new keyword here is extends. This means that our class is an extension, or subclass of PHPUnit's TestCase class,
-which is how PHPUnit is designed to be used. If TestCase has been marked final we wouldn't be able to extend it.
-
If you're not on PHP 7.4, remember to remove the Planet property type for the SUT, and replace it with a docbloc as we
+
The first new keyword here is use. This is syntactic sugar that saves us having to spell out the name of the class
+PHPUnit\Framework\TestCase in full when we use below.
+
We also meet the extends keyword here. This means that our class is an extension, or subclass, of PHPUnit's TestCase
+class, which is how PHPUnit is designed to be used. If TestCase has been marked final we wouldn't be able to extend
+it.
+
If you don't have PHP 7.4, remember to remove the Planet property type of the SUT, and replace it with a docbloc as we
did for the properties of Planet itself.
It's prudent to see a test fail at least once before believing what it says when it passes. To make it fail, comment out
$this->populationSize++; in src/Planet.php:
@@ -115,8 +117,8 @@
Writing a test
vendor/bin/phpunit test
This will search for any filenames ending in Test.php in the test directory. In each test case any public function
whose name starts with test is considered a test. For every test PHPUnit creates an instance of the class, calls the
-setup function, then calls the test function, records the results, and then throws away the object. So if we had two test
-it would run the set up function twice. If an object is referenced only by garbage it is garbage, so when the test case
+setup function, then calls the test function, records the results, and then throws away the object. So if we had two
+tests it would call setUp twice. Any object referenced only by garbage is garbage, so when the test case
object is thrown away the Planet is thrown away too, and any mutations to the planet will not affect the next test.
The output from PHPUnit should look like:
PHPUnit 9.0.1 by Sebastian Bergmann and contributors.
@@ -144,14 +146,14 @@
Writing a test
OK (1 test, 2 assertions)
Writing tests is a huge topic, which we can't cover in detail here. PHPUnit has excellent
-official documentation. You might choose to do
-Test Driven Development (TDD) / Behaviour Driven Development (BDD) and write your tests before writing the
-production code that they cover.
+official documentation. You might want to do
+Test Driven Development (TDD) and/or Behaviour Driven Development (BDD) and write your tests before writing the
+production code that they cover.
Some other major test frameworks for PHP are PHPSpec and
-Behat, which are both designed around the BDD approach, which
-uses the language of executable specifications rather than tests. The major difference is that in PHPSpec, as with
-PHPUnit, you code in PHP. In Behat you code in a separate language called gherkin, designed to be readable by project
-stakeholders who haven't been trained in programming.
+Behat. These are both designed around the BDD approach, which
+uses the language of executable specifications rather than tests. A major difference between them is that in PHPSpec,
+as with PHPUnit, you code in PHP. In Behat you code in a separate language called gherkin, designed to look like English
+and be readable by people who haven't been trained in programming.
(This part should probably be moved out of the intro, perhaps entirely out of the main tutorial content.)
I work as a PHP developer, and have some colleagues with great Javascript skills who sometimes want to do work in the PHP
side of our application, but don't have PHP experience from their previous jobs. I looked around for an introductory
-tutorial to recommend they can work through, but I didn't find anything satisfying.
+tutorial to recommend they can work through, but I didn't find anything satisfying.
The tutorial at php.net appears not to have been updated for several years,
-and doesn't even introduce either the function or class keywords. On the other hand it does show PHP mixed with HTML,
+and doesn't even introduce the function or class keywords. On the other hand it does show PHP mixed with HTML,
which I plan to pretend doesn't exist. I think it's now probably worse than useless.
David Brumbaugh's
Learn a New Language: Migrating from JavaScript to PHP
-is much closer to what I want to write, but it's a bit too short. It doesn't explain how to install PHP, or what composer is,
+is much closer to what I want to write, but it's a bit too short. It doesn't explain how to install PHP, or what Composer is,
and again it prominently covers mixing PHP with HTML. It shows both the pre-and post 5.4 array syntax, whereas I want to
require the reader to install at least PHP 7.2, and spend little if any time on older versions.
PHP The Right Way is often recommended to PHP beginners, for good reasons. It has very good
@@ -94,7 +94,7 @@
PHP applications often connect to databases. This is even harder to avoid in PHP than it would be in other languages
+you can run on a web server like JS, Java, or C#, because PHP has a share-nothing architecture. That means that the
+code dealing with each request runs in isolation, and does not share any objects or variables with other requests. At
+the end of the request-response cycle all stack frames are unwound and all objects are garbage.
+
So if your program needs to persist anything from one page to the next, a database is a natural choice.
+
For simplicity, we will use SQLite. This is not a database server that you connect to,
+but a full featured SQL database engine supplied as a C library, and available as an add-on module for PHP. An SQLite
+database is just a file.
+
The details of SQL (structured query language), the programming language for defining and querying databases, are beyond
+the scope of this tutorial. We will see some SQL code, but as far as PHP is concerned they are just strings to send to
+the database engine.
+
Install the SQLite PHP Module
+
We will use our SQLite database via PHP's PHP Data Objects (PDO) extension, which provides a consistent interface for
+accessing many different DB types.
+
First check if the necessary PHP modules are installed. Run php -i | grep sqlite. If you have the module set up,
+you should see a line like "PDO drivers => sqlite". There may be other drivers listed alongside sqlite.
+
If you don't have the PDO SQLite module, how to install it will depend on your OS and how you installed PHP.
+
Linux
+
If you installed PHP through your package manager, there should be an SQLite module available in the same place. Make
+sure you get the module that matches your PHP version number. For instance, on Debian or Ubuntu search your repository
+with apt-cache search php | grep sqlite. Once you've found the package name, e.g. php7.4-sqlite3 install it with
+a command such as sudo apt install php7.4-sqlite3.
+
Mac
+
If you installed PHP via homebrew, I think should automatically have come with SQLite built in and enabled.
+
Windows
+
If you installed PHP as part of XAMPP this should have SQLite enabled by default.
+
Creating a database
+
First, let's write a PHP script src/create-database.php to create a new database with one table:
Run the script with php create-database.php. It should create a new binary file database.sqlite.
+
file_put_contents writes a string to a file, creating the file if it already exist. We're using here simply to create
+a completely empty file.
+
The PDO class is supplied by the PDO extension. A PDO object represents a connection to a database, and allows us to
+send it queries and get back results. When we call the PDO constructor
+we pass it a string holding details of the database we want to connect to, known as a DSN. For SQLITE the DSN
+consists of sqlite: followed by the absolute file path of our database.
+
Querying the database
+
Let's write a very small program to put the planets in the database, and let us view info about any planet. First we
+will need a class that can insert all the planets. Make a file src/PlanetStore.php
+
<?php declare(strict_types=1);
+
+namespace PhpAsASecondLanguage;
+
+use PDO;
+
+final class PlanetStore
+{
+ private PDO $connection;
+
+ public function __construct(PDO $connection) {
+ $this->connection = $connection;
+ }
+
+ public function storePlanets(): void
+ {
+ $statement = $this->connection->prepare('INSERT INTO planets (name, population_size) values (?, ?);');
+ foreach ($this->generatePlanets() as $planet) {
+ $statement->execute([$planet->getName(), $planet->getPopulationSize()]);
+ }
+ }
+
+ /** @return Planet[] */
+ private function generatePlanets(): array
+ {
+ return [
+ new Planet('Mercury', 0),
+ new Planet('Venus', 0),
+ new Planet('Earth', 7.7 * 10**9),
+ new Planet('Mars', 0),
+ new Planet('Jupiter', 0),
+ new Planet('Saturn', 0),
+ new Planet('Uranus', 0),
+ new Planet('Neptune', 0),
+ ];
+ }
+}
+
And write a script to invoke this class: storePlanets.php:
Run php storePlanets.php. This should add the eight planets to the database. If it works it won't produce any visible
+output.
+
The most important thing to notice about our code so far is that we didn't concatenate any strings to create queries to
+send to the database. Instead we prepared a query, using question marks as placeholders for our data, and then supplied
+the values of those parameters separately each time we executed the query. This practice makes our database use much
+less error prone and more secure. The database engine knows what exactly what's code that it should interpret and what's
+data that it should simply store.
+
Now let's write the code to show a planet. First let's add a function to the PlanetStore class to pull a Planet out of
+the database:
+
public function getPlanet(string $planetName): ?Planet
+ {
+ $statement = $this->connection->prepare(
+ 'SELECT name, population_size FROM planets where name = :name;'
+ );
+ $statement->bindValue(':name', $planetName);
+ $statement->execute();
+
+ $row = $statement->fetch(PDO::FETCH_ASSOC);
+
+ if (! $row) {
+ return null;
+ }
+
+ return new Planet($row['name'], (float) $row['population_size']);
+ }
+
Notice the return type: ?Planet means that the function may either return an instance of Planet, or the value null.
+We can add ? to the beginning of any type name to make it nullable.
+
We use (float) to cast or convert the population size data from a string, as it comes back from the sqlite DB, to
+a floating point number.
+
In this case we have used a named parameter in the database query, instead of a question mark.
+
Finally we need to make a PHP script to go between this function and the browser. Call it viewPlanet.php:
Notice the $_GET array - PHP automatically fills this so-called superglobals array with any query parameters sent by
+the browser. It is available to us anywhere in our program, but it's best to limit where we use it - code that directly
+pulls data from superglobals can be hard to test and re-use.
PDO has drivers for eleven other database engines that you can use
+use instead of SQLite. Connecting, sending queries, and receiving rows should work in the same way, but of course the
+details of connection strings and SQL code will vary from DB to DB.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/index.html b/index.html
index 01de24b..33f7ad9 100644
--- a/index.html
+++ b/index.html
@@ -45,6 +45,7 @@
PHP applications often connect to databases. This is even harder to avoid in PHP than it would be in other languages
-you can run on a web server like JS, Java, or C#, because PHP has a share-nothing architecture. That means that the
+you can run on a web server like JS, Java, or C#, because PHP has a shared-nothing architecture. That means that the
code dealing with each request runs in isolation, and does not share any objects or variables with other requests. At
-the end of the request-response cycle all stack frames are unwound and all objects are garbage.
+the end of the request-response cycle all stack frames are unwound and all objects become garbage.
So if your program needs to persist anything from one page to the next, a database is a natural choice.
For simplicity, we will use SQLite. This is not a database server that you connect to,
-but a full featured SQL database engine supplied as a C library, and available as an add-on module for PHP. An SQLite
-database is just a file.
-
The details of SQL (structured query language), the programming language for defining and querying databases, are beyond
+but a full featured SQL database engine supplied as a C library, and available as an add-on module for PHP, and the
+world's most widely deployed DB engine. An SQLite database is just a file.
+
The details of SQL, the programming language for defining and querying databases, are beyond
the scope of this tutorial. We will see some SQL code, but as far as PHP is concerned they are just strings to send to
the database engine.
Install the SQLite PHP Module
@@ -70,10 +70,12 @@
Install the SQLite PHP Module
Linux
If you installed PHP through your package manager, there should be an SQLite module available in the same place. Make
sure you get the module that matches your PHP version number. For instance, on Debian or Ubuntu search your repository
-with apt-cache search php | grep sqlite. Once you've found the package name, e.g. php7.4-sqlite3 install it with
-a command such as sudo apt install php7.4-sqlite3.
+with:
+
apt-cache search php | grep sqlite
+
Once you've found the package name, e.g. php7.4-sqlite3 install it with a command such as
+
sudo apt install php7.4-sqlite3.
Mac
-
If you installed PHP via homebrew, I think should automatically have come with SQLite built in and enabled.
+
If you installed PHP via homebrew, I think will automatically have come with SQLite built in and enabled.
Windows
If you installed PHP as part of XAMPP this should have SQLite enabled by default.
Run the script with php create-database.php. It should create a new binary file database.sqlite.
-
file_put_contents writes a string to a file, creating the file if it already exist. We're using here simply to create
+
file_put_contents writes a string to a file, overwriting the file if it already exists. We're using here to create
a completely empty file.
-
The PDO class is supplied by the PDO extension. A PDO object represents a connection to a database, and allows us to
-send it queries and get back results. When we call the PDO constructor
-we pass it a string holding details of the database we want to connect to, known as a DSN. For SQLITE the DSN
-consists of sqlite: followed by the absolute file path of our database.
+
The PDO class is supplied by the PDO extension. A PDO object represents
+a connection to a database, and allows us to send it queries and get back results. When we call the
+PDO constructor we pass it a string holding details of the database
+we want to connect to, known as a Data Source Name or DSN. For SQLite the DSN consists of 'sqlite:' followed by an
+absolute file path.
Querying the database
Let's write a very small program to put the planets in the database, and let us view info about any planet. First we
will need a class that can insert all the planets. Make a file src/PlanetStore.php
@@ -144,7 +148,8 @@
Querying the database
];
}
}
-
And write a script to invoke this class: storePlanets.php:
+
Once again if you don't have PHP 7.4 you will need to remove the PDO property type on the $connection property.
+
Now write a script storePlanets.php to use this class:
Run php storePlanets.php. This should add the eight planets to the database. If it works it won't produce any visible
-output.
+
Run php storePlanets.php. This should add the eight planets to the database. It shouldn't produce any visible output.
The most important thing to notice about our code so far is that we didn't concatenate any strings to create queries to
send to the database. Instead we prepared a query, using question marks as placeholders for our data, and then supplied
-the values of those parameters separately each time we executed the query. This practice makes our database use much
-less error prone and more secure. The database engine knows what exactly what's code that it should interpret and what's
-data that it should simply store.
+the values of those parameters separately each time we executed the query.
+
This practice makes our database use much less error prone and more secure. The database engine knows what exactly
+what's code that it should interpret and what's data that it should simply store.
Now let's write the code to show a planet. First let's add a function to the PlanetStore class to pull a Planet out of
the database:
public function getPlanet(string $planetName): ?Planet
@@ -212,9 +216,9 @@
Notice the $_GET array - PHP automatically fills this so-called superglobals array with any query parameters sent by
+
Notice the $_GET array - PHP automatically fills this so-called superglobal array with any query parameters sent by
the browser. It is available to us anywhere in our program, but it's best to limit where we use it - code that directly
-pulls data from superglobals can be hard to test and re-use.
+pulls data from superglobals can be very hard to test and re-use and understand.
Our planet viewer works, but it isn't pretty. Let's use some HTML to make it look a little better.
+
It has been a common practice to use PHP as a templating language, and even mix PHP and HTML code together in the same
+file. While PHP can still be used as a templating language, it isn't a good one, so we won't do that.
+
Instead, we will use PHP to prepare whatever data we want to show, and then pass that to a template written in a
+different language.
+
There are dozens of template engines available as libraries to use in PHP programs, and another good option
+is to make a single page application, with all templating done in the browser, and data sent from the server as JSON.
+
Twig
+
For this tutorial we will write a template in the Twig language, which works with PHP.
+
First install Twig in your project by running:
+
composer require "twig/twig:^3.0"
+
Now let's make our template. Start by writing a page in HTML. For now we'll hard code everything. Make a
+'templates' directory, and save the following as 'templates/planet.html.twig':
If you run the PHP server again and view your site you should see the content of the template. Of course at this point
+all we're doing is serving static HTML with extra steps.
+
Behind the scenes the Twig engine compiles the template to a PHP class. We don't need to read the class, but this is
+useful because it means we can pass PHP objects to it and use their properties and functions. Let's pass our planet
+object to the template. Change the last line of 'viewPlanet.php' to:
+
echo $template->render(['planet' => $planet]);
+
Finally we need to edit 'planet.html.twig'. Since we won't always be reading the template at the same time as the PHP file
+that uses it, and neither will any tools and IDEs we might be using, we should add a comment to make it clear that we
+expect to have a planet variable, and it will be of type Planet. Add the following to the top of the file:
+
{# @var planet PhpAsASecondLanguage\Planet #}
+
In Twig we use double curly brackets to output dynamic data: Replace Some Planet with
+{{planet.name}}, and replace 34601 with {{planet.populationSize}}. Twig knows that we mean getName and
+getPopulationSize, so we don't have to write those function names out in full.
+
If you reload the page you should now see the planet details in their proper places.
+
Twig is a full featured special purpose programming language, with features like loops, conditionals, filters,
+inheritance, etc, but this is a PHP tutorial, not a Twig tutorial.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/index.html b/index.html
index 9e9b4a6..4028a7d 100644
--- a/index.html
+++ b/index.html
@@ -46,6 +46,7 @@
+
+
\ No newline at end of file
diff --git a/CNAME b/CNAME
index cdd7147..4108d97 100644
--- a/CNAME
+++ b/CNAME
@@ -1 +1 @@
-a-moderately-short-php-tutorial.com
\ No newline at end of file
+a-moderately-short-php-tutorial.com
diff --git a/index.html b/index.html
index 10d875b..018a7cf 100644
--- a/index.html
+++ b/index.html
@@ -47,6 +47,8 @@
diff --git a/02-getting-php.html b/02-getting-php.html
index 6c35d1f..c28b616 100644
--- a/02-getting-php.html
+++ b/02-getting-php.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
diff --git a/03-hello-world.html b/03-hello-world.html
index 7e9c951..8bdafba 100644
--- a/03-hello-world.html
+++ b/03-hello-world.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
diff --git a/04-http.html b/04-http.html
index 2c72650..98c3f7a 100644
--- a/04-http.html
+++ b/04-http.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
diff --git a/05-variables.html b/05-variables.html
index 181b778..673dace 100644
--- a/05-variables.html
+++ b/05-variables.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
diff --git a/06-functions.html b/06-functions.html
index 524d44c..c91ab42 100644
--- a/06-functions.html
+++ b/06-functions.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
diff --git a/07-classes-1.html b/07-classes-1.html
index 2be9a75..748199b 100644
--- a/07-classes-1.html
+++ b/07-classes-1.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
<?php declare(strict_types=1);
-namespace PhpAsASecondLanguage;
+namespace AModeratelyShortPhpTutorial;
final class Planet
{
@@ -112,7 +111,7 @@
What's in the class
The entire file is in a namespace. This is useful to help distinguish our code from other people's code, and
to distinguish between submodules when our program gets bigger. We will keep all our code in this namespace. The
-namespace is effectively a prefix, so the full name of the class is \PhpAsASecondLanguage\Planet.
+namespace is effectively a prefix, so the full name of the class is \AModeratelyShortPhpTutorial\Planet.
Planet is a final class. This prevents any other classes being written as subclasses of Planet. Subclassing is
@@ -166,7 +165,7 @@
What's in the class
-
Site built Sat, 29 Feb 2020 18:38.
+
Site built Sat, 29 Feb 2020 20:43.
diff --git a/08-classes-2.html b/08-classes-2.html
index 5d47065..73bb7a5 100644
--- a/08-classes-2.html
+++ b/08-classes-2.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
diff --git a/09-classes-3.html b/09-classes-3.html
index 7d2561f..0c208be 100644
--- a/09-classes-3.html
+++ b/09-classes-3.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
diff --git a/10-testing.html b/10-testing.html
index 2e7e121..b804dab 100644
--- a/10-testing.html
+++ b/10-testing.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
There was 1 failure:
-1) PhpAsASecondLanguage\PlanetTest::test_it_can_accept_immigrant
+1) AModeratelyShortPhpTutorial\PlanetTest::test_it_can_accept_immigrant
Failed asserting that 0.0 is identical to 1.0.
/tmp/composerPlayground/test/PlanetTest.php:20
@@ -173,7 +172,7 @@
Writing a test
-
Site built Sat, 29 Feb 2020 18:38.
+
Site built Sat, 29 Feb 2020 20:43.
diff --git a/11-databases.html b/11-databases.html
index 3ed65e6..8f4b958 100644
--- a/11-databases.html
+++ b/11-databases.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
diff --git a/12-templating.html b/12-templating.html
index 96b5e68..ca4c929 100644
--- a/12-templating.html
+++ b/12-templating.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
Finally we need to edit 'planet.html.twig'. Since we won't always be reading the template at the same time as the PHP file
that uses it, and neither will any tools and IDEs we might be using, we should add a comment to make it clear that we
expect to have a planet variable, and it will be of type Planet. Add the following to the top of the file:
In Twig we use double curly brackets to output dynamic data: Replace Some Planet with
{{planet.name}}, and replace 34601 with {{planet.populationSize}}. Twig knows that we mean getName and
getPopulationSize, so we don't have to write those function names out in full.
@@ -136,7 +135,7 @@
Twig
-
Site built Sat, 29 Feb 2020 18:38.
+
Site built Sat, 29 Feb 2020 20:43.
diff --git a/13-static-analysis.html b/13-static-analysis.html
deleted file mode 100644
index 4c80e94..0000000
--- a/13-static-analysis.html
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-
-
-
-
- PHP as a Second Language
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/14-further-reading.html b/14-further-reading.html
index 4c80e94..e34b3d2 100644
--- a/14-further-reading.html
+++ b/14-further-reading.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
diff --git a/index.html b/index.html
index 018a7cf..82ad769 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
- PHP as a Second Language
+ A Moderately Short PHP Tutorial
I recommend using at least one static analysis tool on any PHP project. While languages like TypeScript and Java have
+explicit compilation phases that can quickly catch lots of errors, PHP doesn't. Most of the type checks in PHP happen only
+as each line of code is executed.
+
Unit tests help, but 100% test coverage is unlikely, and even then we can only ever test with a few example inputs. For
+more confidence we need static analysis as well, especially if we want to be able to easily refactor our code and upgrade
+the libraries we're using.
+
Static Analysers for PHP include Psalm, PHPStan and
+Phan. In this tutorial we will use Psalm.
+
Running Psalm
+
Install Psalm with Composer:
+
composer require --dev vimeo/psalm
+
Create a Psalm Config file:
+
./vendor/bin/psalm --init
+
Finally, run Psalm to check your code:
+
./vendor/bin/psalm
+
You should see something like:
+
Calculating best config level based on project files
+Calculating best config level based on project files
+Scanning files...
+Analyzing files...
+
+░E
+
+Detected level 7 as a suitable initial default
+Config file created successfully. Please re-run psalm.
+
Re-run Psalm as instructed. If your code is the same as mine, you should see "No errors found!", and
+"1 other issues found. You can display them with --show-info=true".
+
That's OK, but where's the fun in a static analysis tool that doesn't complain about anything? Let's change the Psalm
+settings to make it a lot stricter. Open 'psalm.xml' and change errorLevel="7" to errorLevel="1".
+
Re-run Psalm. Now you should see an error:
+
+ERROR: InvalidArgument - src/PlanetStore.php:52:27 - Argument 1 of AModeratelyShortPhpTutorial\Planet::__construct expects string, scalar provided
+ return new Planet($row['name'], (float)$row['population_size']);
+
+
Psalm has looked at our code, and the code of the libraries and modules we're using, and found a mismatch. The planet
+class constructor needs a string, but we've passed it something from the array returned by the
+PDOStatement::fetch. Psalm knows that when we call fetch with
+the \PDO::FETCH_ASSOC we will either get false or an array of scalar values (i.e. not objects). It can see that the
+value isn't false at line 52, because when it is false the function returns early, skipping that line.
+
In this case we know our database a bit better than Psalm does, and we need to add a comment to tell Psalm that fetch
+will return either false or an array of strings. Edit 'PlanetStore.php' and add docblock for the $row variable:
This docbloc means that the $row variable's type is the union of false and string-indexed, string-valued array. In
+other words it either holds the value false, or it holds an array, in which any keys are strings and any values are
+also strings. | combines types to make a union type. The < > brackets are used to provide arguments for a
+generic type. false is an example of a literal type.
+
Psalm's type system extends the PHP type system, and is a lot more expressive. PHP itself does not have union, generic,
+or literal types, although unions are coming in PHP 8. We can use types that PHP doesn't support by writing them in
+docblocks, with tags such as
+@var,
+@param, and
+@return,
+and making sure we run our static analysis tool every time
+we edit our source code.
+
Let's add a deliberate mistake to our code to see a bit more of what Psalm can help us with. Let's suppose we forgot
+that the database might not have the planet we're looking for. Comment out the check for that in 'planetStore.php':
+
// if (! $row) {
+// return null;
+// }
+
Re-run Psalm. It now reports two PossiblyInvalidArrayAccess errors, telling us that we
+'Cannot access array value on non-array variable $row of type false'. This is a reminder that we should have made sure
+that $row is not false before trying to use it as an array. If we leave it like this our server will produce an HTTP
+500 internal server error instead of displaying the 404 page when someone asks to see the planet Pluto. Put the check
+back to make Psalm happy again.
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/14-further-reading.html b/14-further-reading.html
index e34b3d2..dcaac57 100644
--- a/14-further-reading.html
+++ b/14-further-reading.html
@@ -47,6 +47,7 @@
Psalm has looked at our code, and the code of the libraries and modules we're using, and found a mismatch. The planet
class constructor needs a string, but we've passed it something from the array returned by the
-PDOStatement::fetch. Psalm knows that when we call fetch with
-the \PDO::FETCH_ASSOC we will either get false or an array of scalar values (i.e. not objects). It can see that the
-value isn't false at line 52, because when it is false the function returns early, skipping that line.
Psalm knows that when we call fetch with \PDO::FETCH_ASSOC we will either get false or an array of scalar
+values (i.e. not objects). It can see that the value isn't false at line 52, because when it is false the function
+returns early, skipping that line.
In this case we know our database a bit better than Psalm does, and we need to add a comment to tell Psalm that fetch
will return either false or an array of strings. Edit 'PlanetStore.php' and add docblock for the $row variable:
This has only been a cursory introduction to the PHP language and ecosystem. Here are some ideas for things to try next,
+in no particular order:
+
+
+
If you enjoyed this tutorial, or found it useful please let me know and share it. If you think it could be improved,
+let me know that too. You can find me on twitter.
+
+
+
Take a break from the screen - there's good evidence that you remember more of what you've learned shortly before or
+after a break.
+
+
+
Write your own PHP program, or dive into a PHP codebase and make some improvement.
+
+
+
Browse the official PHP Docs. There's a lot in the language that we haven't covered.
+
+
+
If you find a symbol that you don't understand in PHP code, look it up in the
+What does this symbol mean in PHP?
+Stack Overflow reference page.
Read the Twig Documentation and learn how to make more sophisticated templates.
+
+
+
Watch the talk Aggressive PHP Quality Assurance in 2019 by Marco
+Pivetta, to learn that "PHP is a horrible language that allows you to do too many
+things", and how to use the tools that will tell you not to do the bad things. In this tutorial I've tried to show only
+the good things you can do in PHP.
+
+
+
Learn how to use a PHP Framework or CMS. I would personally recommend Symfony, and the
+Symfony Getting Started guide.
Read PHP: The Right Way. This covers a lot more topics than we've been able to include
+here, including a lot of the history of how PHP got to be the language it is today.
+
+
+
Browse PHP packages on Packagist. This is where Composer looks by default when you tell it
+to install a library or tool.
+
+
+
Subscribe to PHP Weekly News for interesting links every Thursday.
Accelerate describes the capabilities that
+organisations need to develop and maintain to perform well with software. You can also read about those capabilities in
+the briefer and slightly more up-to date form of the
+2019 Accelerate State of DevOps Report.
+
+
+
+
+
+ Copyright Barney Laurance 2020
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/index.html b/index.html
index da3d340..0a0c2d8 100644
--- a/index.html
+++ b/index.html
@@ -48,7 +48,7 @@
If you enjoyed this tutorial, or found it useful please let me know and share it. If you think it could be improved,
-let me know that too. You can find me on twitter.
This is for anyone who wants to learn to program in PHP as a second language.
-
You should already know at least one programming language with curly braces, if conditions, variables, functions and
+
Introduction
+
Who is this for?
+
This is for anyone who wants to learn to program in PHP.
+
You should probably already know at least one programming language with curly braces, if conditions, variables, functions and
loops. The most likely scenario is that you are a Javascript or Typescript developer, working on a team that uses
a combination of PHP and JS or TS, and while you might not currently need deep PHP knowledge,
you want 'T-shaped skills', involving basic PHP knowledge. There will be some comparisons between with Javascript and
Typescript.
-
Why did I write it?
-
(This part should probably be moved out of the intro, perhaps entirely out of the main tutorial content.)
+
Why did I write it?
I work as a PHP developer, and have some colleagues with great Javascript skills who sometimes want to do work in the PHP
side of our application, but don't have PHP experience from their previous jobs. I looked around for an introductory
-tutorial to recommend they can work through, but I didn't find anything satisfying.
-
The tutorial at php.net appears not to have been updated for several years,
-and doesn't even introduce the function or class keywords. On the other hand it does show PHP mixed with HTML,
-which I plan to pretend doesn't exist. I think it's now probably worse than useless.
-
David Brumbaugh's
-Learn a New Language: Migrating from JavaScript to PHP
-is much closer to what I want to write, but it's a bit too short. It doesn't explain how to install PHP, or what Composer is,
-and again it prominently covers mixing PHP with HTML. It shows both the pre-and post 5.4 array syntax, whereas I want to
-require the reader to install at least PHP 7.2, and spend little if any time on older versions.
-
PHP The Right Way is often recommended to PHP beginners, for good reasons. It has very good
-advice, but it's not really a tutorial to work through step by step. I think it could be overwhelming as an introduction
-to PHP, and covers a lot more than I think most new PHP developers would want to know, including several paragraphs about
-the MySQL Extension, which as it says "is incredibly old and has been superseded by two other extensions". I plan to
-concentrate on the parts of PHP that I can recommend.
-
For PHP devs learning some Javascript, typescript, and/or React there seem to be great materials available online,
-such as the React Tutorial,
-HTML Dog's Javascript Tutorials,
-TypeScript in 5 minutes and the (paid) Typescript course at
-Execute Program. As far as I can tell there's nothing similar available free to help
-JS devs learn some PHP.
+tutorial to recommend they can work through, but I didn't find anything that seemed exactly right.
+
Approach
+
This is a step-by-step guide to using a selection of the most important aspects of PHP. It doesn't attempt to be
+comprehensive.
+
PHP is an old language that has evolved organically over the years. There are many ways to do things,
+and many parts of the language that are best avoided in new code. This tutorial will concentrate on recommended
+ways to use PHP, and ignore most of the things that PHP allows you to do but you probably shouldn't.
-
-
- Copyright Barney Laurance
- Site generated from sources at 00:11 London time on
- Sun 01 Mar 2020.
-
+
+
+
If you just want to run a PHP one-liner, or experiment with a tiny throwaway PHP script, the easiest way is probably
+
Getting PHP
+
If you just want to run a PHP one-liner, or experiment with a tiny throwaway PHP script, the easiest way is probably
online at 3v4l, but to work through this tutorial you should have PHP installed on your computer.
-
Installation
+
Installation
First check if you already have PHP installed. Open your command-line prompt, and type php --v. If you have PHP, you
should see something like:
At this point we're ready to start writing in PHP. Make a new folder for your PHP code.
+
Hello, world
+
At this point we're ready to start writing in PHP. Make a new folder for your PHP code.
If you have a favourite text editor or integrated development environment, such as Vim, Atom, or VS Code you may want to
stick with that, but the best tool for editing PHP code is almost certainly JetBrains'
PhpStorm. PhpStorm is free for 30 days, but will demand money after that.
@@ -73,12 +105,29 @@
Hello, world.
20% shorter to write.
-
-
- Copyright Barney Laurance
- Site generated from sources at 00:11 London time on
- Sun 01 Mar 2020.
-
+
+
+
PHP is of course mostly used in web servers, so you might be wondering why we started with a command line script.
+
HTTP
+
PHP is of course mostly used in web servers, so you might be wondering why we started with a command line script.
In fact you can access the same script through your web browser. PHP comes with a built-in web server for development
and testing uses. To start it, run:
php -S localhost:8080
@@ -64,12 +96,29 @@
HTTP
Wordpress or Drupal.
-
-
- Copyright Barney Laurance
- Site generated from sources at 00:11 London time on
- Sun 01 Mar 2020.
-
+
+
+
Variables in PHP always have a dollar sign $ at the start of their names, and you declare a variable by assigning a
+
Variables, arrays and loops
+
Variables in PHP always have a dollar sign $ at the start of their names, and you declare a variable by assigning a
value to it. PHP interpolates double-quoted strings with variables. Edit hello.php to use a variable:
<?php declare(strict_types=1);
@@ -60,7 +92,7 @@
Variables, arrays and loops
echo "Hello, $planet.\n";
You can probably guess what this will do when you run it on your command line or serve it to your browser.
-
Arrays and loops
+
Arrays and loops
PHP has a very versatile built-in type called array. You're unlikely to find much PHP code that doesn't use arrays
extensively, but they are easy to overuse at the cost of other more expressive types. Despite the name, a PHP array
isn't really an array as you may know it from other languages. A PHP array is an ordered iterable map, with keys
@@ -108,7 +140,7 @@
Arrays and loops
Re-run the script - the planets are now listed in reverse order. But notice that the array keys have not changed -
Mercury is still planet number 0, it's just that 0 now comes last. PHP array keys can come in any
order.
-
Associative arrays
+
Associative arrays
We can also assign array keys explicitly. When we're interested in the keys of an array we call it an
associative array. For example:
PHP supports class-based object oriented programming, heavily influenced by Java and similar languages. This is
+
Classes and Objects
+
PHP supports class-based object oriented programming, heavily influenced by Java and similar languages. This is
different to the prototype based OO in JavaScript.
In the sort of PHP code I write almost 100% of the code in a project is in classes - some classes are used to make
objects, and others may just be convenient wrappers around groups of functions.
@@ -84,7 +116,7 @@
Classes and Objects
return $this->populationSize;
}
}
-
Older PHP Versions
+
Older PHP Versions
name and populationSize are the properties of the class, and they have string and float types respectively.
Before 7.4 PHP didn't allow us to specify types for properties. We still want to know what types of values we intend to
put in the properties, so we use a DocBlock instead. If you don't have 7.4 yet, change the property declarations
@@ -101,10 +133,10 @@
Older PHP Versions
These DocBlocks are ignored by the PHP engine, but they are very useful for us, and many tools and IDEs will read them.
The code inside the docblock is written in the PHPDoc
language.
-
Running the code
+
Running the code
Run php Planet.php. You should see no output - a class by itself doesn't do anything. We will write code to use this
class on the next page.
-
What's in the class
+
What's in the class
Let's read through the class from top to bottom.
@@ -151,12 +183,29 @@
What's in the class
JavaScript module that exports some but not all of its symbols.
-
-
- Copyright Barney Laurance
- Site generated from sources at 00:11 London time on
- Sun 01 Mar 2020.
-
+
+
+
On the last page we wrote Planet class, and saved it in a file called Planet.php. Now we want to put that class
+
Classes and Composer
+
On the last page we wrote Planet class, and saved it in a file called Planet.php. Now we want to put that class
to work.
PHP works by executing whatever script we give it from beginning to end. It won't run anything inside a class unless
we that script tells it to, so every PHP program needs at least one line that isn't part of any class as an entry-point.
@@ -71,7 +103,7 @@
Classes Part 2
The new keyword creates objects instances from classes, and automatically calls any constructor the class has.
We can try running this now but it won't work just yet, because we need to link it up with Planet.php. When you type
php start.php you should see PHP Fatal error: Uncaught Error: Class 'Planet' not found.
-
Linking files together
+
Linking files together
There are two main ways to link files together. The old way is require_once, and the new ways is composer. We'll
start with require_once.
Require_once
@@ -141,12 +173,29 @@
Composer
If you run php start.php you should once again learn the population of Neptune.
-
-
- Copyright Barney Laurance
- Site generated from sources at 00:11 London time on
- Sun 01 Mar 2020.
-
+
+
+
Not all the methods on a class have to run in the context of an object. Methods that work without a $this object
instance are called static methods. Let's add a static method to the Planet class in src/Planet.php:
echo "Planet {$planet->getName()} has a population of {$planet->getPopulationSize()}.\n";
The double colon is the Scope Resolution Operator. It accesses static methods (and properties) of classes - there is
no need to have an object first. In this case our method returns an instance of the class, but it could do anything.
-
Object Identity
+
Object Identity
A PHP variable can't actually hold an object - instead it holds an object identifier, also known as a reference.
Two objects created the same way, with the same properties will have distinct identities. But if one object is created
and then assigned to two variables, they will both hold identifiers for the same object. This becomes important when we
@@ -151,12 +183,29 @@
Object Identity
confusing if not managed carefully.
-
-
- Copyright Barney Laurance
- Site generated from sources at 00:11 London time on
- Sun 01 Mar 2020.
-
+
+
+
If we want to work on even a moderately complex program over time, we need automated testing - manually testing
+
Testing
+
If we want to work on even a moderately complex program over time, we need automated testing - manually testing
everything every time we make a change would quickly become unsustainable.
-
Installing PHPUnit
+
Installing PHPUnit
The leading test framework for PHP is Sebastian Bergmann's PHPUnit.
Since we already have composer set up for our project, we can use that to install PHPUnit in the vendor directory. Run:
composer require --dev phpunit/phpunit
@@ -81,7 +113,7 @@
Installing PHPUnit
installed. At the time of writing it shows me that PHPUnit is at version 9.0.1, and I can see the details of 29 other
packages that have been installed because PHPUnit depends on them directly or indirectly. The composer show command
will output the list of installed packages in a much more consice format.
-
Writing a test
+
Writing a test
Let's write our first test. Create a test subdirectory next to src, and write the following in test/PlanetTest.php
PHP applications often connect to databases. This is even harder to avoid in PHP than it would be in other languages
+
Databases
+
PHP applications often connect to databases. This is even harder to avoid in PHP than it would be in other languages
you can run on a web server like JS, Java, or C#, because PHP has a shared-nothing architecture. That means that the
code dealing with each request runs in isolation, and does not share any objects or variables with other requests. At
the end of the request-response cycle all stack frames are unwound and all objects become garbage.
@@ -62,7 +94,7 @@
Databases
The details of SQL, the programming language for defining and querying databases, are beyond
the scope of this tutorial. We will see some SQL code, but as far as PHP is concerned they are just strings to send to
the database engine.
-
Install the SQLite PHP Module
+
Install the SQLite PHP Module
We will use our SQLite database via PHP's PHP Data Objects (PDO) extension, which provides a consistent interface for
accessing many different DB types.
First check if the necessary PHP modules are installed. Run php -i | grep sqlite. If you have the module set up,
@@ -79,7 +111,7 @@
Mac
If you installed PHP via homebrew, I think will automatically have come with SQLite built in and enabled.
Windows
If you installed PHP as part of XAMPP this should have SQLite enabled by default.
-
Creating a database
+
Creating a database
First, let's write a PHP script src/create-database.php to create a new database with one table:
PDO constructor we pass it a string holding details of the database
we want to connect to, known as a Data Source Name or DSN. For SQLite the DSN consists of 'sqlite:' followed by an
absolute file path.
-
Querying the database
+
Querying the database
Let's write a very small program to put the planets in the database, and let us view info about any planet. First we
will need a class that can insert all the planets. Make a file src/PlanetStore.php
PDO has drivers for eleven other database engines that you can use
use instead of SQLite. Connecting, sending queries, and receiving rows should work in the same way, but of course the
details of connection strings and SQL code will vary from DB to DB.
-
-
- Copyright Barney Laurance
- Site generated from sources at 00:11 London time on
- Sun 01 Mar 2020.
-
+
+
+
Our planet viewer works, but it isn't pretty. Let's use some HTML to make it look a little better.
+
Templating
+
Our planet viewer works, but it isn't pretty. Let's use some HTML to make it look a little better.
It has been a common practice to use PHP as a templating language, and even mix PHP and HTML code together in the same
file. While PHP can still be used as a templating language, it isn't a good one, so we won't do that.
Instead, we will use PHP to prepare whatever data we want to show, and then pass that to a template written in a
different language.
There are dozens of template engines available as libraries to use in PHP programs, and another good option
is to make a single page application, with all templating done in the browser, and data sent from the server as JSON.
-
Twig
+
Twig
For this tutorial we will write a template in the Twig language, which works with PHP.
First install Twig in your project by running:
composer require "twig/twig:^3.0"
@@ -112,21 +144,38 @@
Twig
Finally we need to edit 'planet.html.twig'. Since we won't always be reading the template at the same time as the PHP file
that uses it, and neither will any tools and IDEs we might be using, we should add a comment to make it clear that we
expect to have a planet variable, and it will be of type Planet. Add the following to the top of the file:
In Twig we use double curly brackets to output dynamic data: Replace Some Planet with
-{{planet.name}}, and replace 34601 with {{planet.populationSize}}. Twig knows that we mean getName and
+, and replace `34601` with. Twig knows that we mean getName and
getPopulationSize, so we don't have to write those function names out in full.
If you reload the page you should now see the planet details in their proper places.
Twig is a full featured special purpose programming language, with features like loops, conditionals, filters,
inheritance, etc, but this is a PHP tutorial, not a Twig tutorial.
-
-
- Copyright Barney Laurance
- Site generated from sources at 00:11 London time on
- Sun 01 Mar 2020.
-
+
+
+
I recommend using at least one static analysis tool on any PHP project. While languages like TypeScript and Java have
+
Static Analysis
+
I recommend using at least one static analysis tool on any PHP project. While languages like TypeScript and Java have
explicit compilation phases that can quickly catch lots of errors, PHP doesn't. Most of the type checks in PHP happen only
as each line of code is executed.
Unit tests help, but 100% test coverage is unlikely, and even then we can only ever test with a few example inputs. For
@@ -59,7 +91,7 @@
Static Analysis
the libraries we're using.
Static Analysers for PHP include Psalm, PHPStan and
Phan. In this tutorial we will use Psalm.
-
Running Psalm
+
Running Psalm
Install Psalm with Composer:
composer require --dev vimeo/psalm
Create a Psalm Config file:
@@ -119,12 +151,29 @@
Running Psalm
back to make Psalm happy again.
-
-
- Copyright Barney Laurance
- Site generated from sources at 00:11 London time on
- Sun 01 Mar 2020.
-
+
+
+
This is for anyone who wants to learn to program in PHP as a second language.
-
You should already know at least one programming language with curly braces, if conditions, variables, functions and
+
Introduction
+
Who is this for?
+
This is for anyone who wants to learn to program in PHP.
+
You should probably already know at least one programming language with curly braces, if conditions, variables, functions and
loops. The most likely scenario is that you are a Javascript or Typescript developer, working on a team that uses
a combination of PHP and JS or TS, and while you might not currently need deep PHP knowledge,
you want 'T-shaped skills', involving basic PHP knowledge. There will be some comparisons between with Javascript and
Typescript.
-
Why did I write it?
-
(This part should probably be moved out of the intro, perhaps entirely out of the main tutorial content.)
+
Why did I write it?
I work as a PHP developer, and have some colleagues with great Javascript skills who sometimes want to do work in the PHP
side of our application, but don't have PHP experience from their previous jobs. I looked around for an introductory
-tutorial to recommend they can work through, but I didn't find anything satisfying.
-
The tutorial at php.net appears not to have been updated for several years,
-and doesn't even introduce the function or class keywords. On the other hand it does show PHP mixed with HTML,
-which I plan to pretend doesn't exist. I think it's now probably worse than useless.
-
David Brumbaugh's
-Learn a New Language: Migrating from JavaScript to PHP
-is much closer to what I want to write, but it's a bit too short. It doesn't explain how to install PHP, or what Composer is,
-and again it prominently covers mixing PHP with HTML. It shows both the pre-and post 5.4 array syntax, whereas I want to
-require the reader to install at least PHP 7.2, and spend little if any time on older versions.
-
PHP The Right Way is often recommended to PHP beginners, for good reasons. It has very good
-advice, but it's not really a tutorial to work through step by step. I think it could be overwhelming as an introduction
-to PHP, and covers a lot more than I think most new PHP developers would want to know, including several paragraphs about
-the MySQL Extension, which as it says "is incredibly old and has been superseded by two other extensions". I plan to
-concentrate on the parts of PHP that I can recommend.
-
For PHP devs learning some Javascript, typescript, and/or React there seem to be great materials available online,
-such as the React Tutorial,
-HTML Dog's Javascript Tutorials,
-TypeScript in 5 minutes and the (paid) Typescript course at
-Execute Program. As far as I can tell there's nothing similar available free to help
-JS devs learn some PHP.
+tutorial to recommend they can work through, but I didn't find anything that seemed exactly right.
+
Approach
+
This is a step-by-step guide to using a selection of the most important aspects of PHP. It doesn't attempt to be
+comprehensive.
+
PHP is an old language that has evolved organically over the years. There are many ways to do things,
+and many parts of the language that are best avoided in new code. This tutorial will concentrate on recommended
+ways to use PHP, and ignore most of the things that PHP allows you to do but you probably shouldn't.
-
-
- Copyright Barney Laurance
- Site generated from sources at 00:11 London time on
- Sun 01 Mar 2020.
-
+
+
+
-
\ No newline at end of file
+
diff --git a/03-hello-world.html b/03-hello-world.html
index 1af6322..3d443e2 100644
--- a/03-hello-world.html
+++ b/03-hello-world.html
@@ -3,6 +3,8 @@
+
+
Hello, world | A Moderately Short PHP Tutorial
@@ -120,8 +122,8 @@
-
\ No newline at end of file
+
diff --git a/05-variables.html b/05-variables.html
index 7be93ed..ecfe9a3 100644
--- a/05-variables.html
+++ b/05-variables.html
@@ -3,6 +3,8 @@
+
+
Variables, arrays and loops | A Moderately Short PHP Tutorial
@@ -174,8 +176,8 @@
-
\ No newline at end of file
+
diff --git a/07-classes-1.html b/07-classes-1.html
index c8e402b..6d011a7 100644
--- a/07-classes-1.html
+++ b/07-classes-1.html
@@ -3,6 +3,8 @@
+
+
Classes and Objects | A Moderately Short PHP Tutorial
@@ -198,8 +200,8 @@
-
\ No newline at end of file
+
diff --git a/08-classes-2.html b/08-classes-2.html
index daf0569..5b5408e 100644
--- a/08-classes-2.html
+++ b/08-classes-2.html
@@ -3,6 +3,8 @@
+
+
Classes and Composer | A Moderately Short PHP Tutorial
@@ -188,8 +190,8 @@
-
\ No newline at end of file
+
diff --git a/14-what-next.html b/14-what-next.html
index 1c2cdba..3a0704d 100644
--- a/14-what-next.html
+++ b/14-what-next.html
@@ -3,6 +3,8 @@
+
+
What next? | A Moderately Short PHP Tutorial
@@ -170,8 +172,8 @@
You should probably already know at least one programming language with curly braces, if conditions, variables, functions and
loops. The most likely scenario is that you are a Javascript or Typescript developer, working on a team that uses
a combination of PHP and JS or TS, and while you might not currently need deep PHP knowledge,
-you want 'T-shaped skills', involving basic PHP knowledge. There will be some comparisons between with Javascript and
+you want 'T-shaped skills', involving basic PHP knowledge. There will be some comparisons with Javascript and
Typescript.
Why did I write it?
I work as a PHP developer, and have some colleagues with great Javascript skills who sometimes want to do work in the PHP
@@ -119,8 +119,8 @@
You should probably already know at least one programming language with curly braces, if conditions, variables, functions and
loops. The most likely scenario is that you are a Javascript or Typescript developer, working on a team that uses
a combination of PHP and JS or TS, and while you might not currently need deep PHP knowledge,
-you want 'T-shaped skills', involving basic PHP knowledge. There will be some comparisons between with Javascript and
+you want 'T-shaped skills', involving basic PHP knowledge. There will be some comparisons with Javascript and
Typescript.
Why did I write it?
I work as a PHP developer, and have some colleagues with great Javascript skills who sometimes want to do work in the PHP
@@ -119,8 +119,8 @@
By convention, and to follow the PSR-1 coding standard, we always put the entry
point in a separate file to the class declaration.
Let's write a simple script to create a planet and print out its details. Since at the moment Planet is just a data
-holder the script won't be too exiting.
Composer has also created a new file for us, composer.lock. This has metadata about the exact versions of the packages
installed. At the time of writing it shows me that PHPUnit is at version 9.0.1, and I can see the details of 29 other
packages that have been installed because PHPUnit depends on them directly or indirectly. The composer show command
-will output the list of installed packages in a much more consice format.
+will output the list of installed packages in a much more concise format.
Writing a test
Let's write our first test. Create a test subdirectory next to src, and write the following in test/PlanetTest.php
If you just want to run a PHP one-liner, or experiment with a tiny throwaway PHP script, the easiest way is probably
online at 3v4l, but to work through this tutorial you should have PHP installed on your computer.
Installation
-
First check if you already have PHP installed. Open your command-line prompt, and type php --v. If you have PHP, you
+
First check if you already have PHP installed. Open your command-line prompt, and type php -v. If you have PHP, you
should see something like:
PHP 7.4.2 (cli) (built: Jan 23 2020 11:21:30) ( NTS )
Copyright (c) The PHP Group
@@ -130,8 +130,8 @@
expect to have a planet variable, and it will be of type Planet. Add the following to the top of the file:
In Twig we use double curly brackets to output dynamic data: Replace Some Planet with
-, and replace `34601` with. Twig knows that we mean getName and
+{{planet.name}}, and replace 34601 with {{planet.populationSize}}. Twig knows that we mean getName and
getPopulationSize, so we don't have to write those function names out in full.
If you reload the page you should now see the planet details in their proper places.
Twig is a full featured special purpose programming language, with features like loops, conditionals, filters,
@@ -157,8 +157,8 @@