diff --git a/assets/css/site.css b/assets/css/site.css index 017507b..e232d05 100644 --- a/assets/css/site.css +++ b/assets/css/site.css @@ -13,8 +13,8 @@ body { padding-bottom: 1em; } pre, code { - font-family: Consolas, monaco, monospace; - font-size: 16px; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 14px; margin: 0 !important; border-radius: 0 !important; padding-top: 0.5em !important; @@ -32,10 +32,12 @@ hr { border: 0; height: 1px; background-color: #EEE; + margin: 0.5em 0; } p { - max-width: 40em; line-height: 1.5; + margin-top: 1.5em; + margin-bottom: 0.75em; } ol { padding-left: 1em; @@ -99,9 +101,10 @@ button:hover .icon svg { clear: both; } .container { - width: 1050px; - margin-left: auto; - margin-right: auto; + max-width: 1050px; +} +.container.small { + max-width: 750px; } .center { margin-left: auto; @@ -159,8 +162,8 @@ button:hover .icon svg { .navigate-links a:hover { text-decoration: none; } -.table-of-contents { - padding: 2em; +.table-of-contents h4 { + margin-top: 0; } .table-of-contents ol { margin: 1em 0; @@ -174,22 +177,21 @@ button:hover .icon svg { width: 1em; display: inline-block; } -.home-title-wrapper { - display: grid; - grid-template-columns: 175px 1fr; - grid-template-rows: 1fr 1fr; -} .home-title { - + text-align: center; } .home-subtitle { + text-align: center; margin-top: 0; grid-column-start: 2; grid-column-end: 3; } .home-logo { - grid-row-start: 1; - grid-row-end: 3; + display: block; + margin: 0 auto; + width: 150px; + height: 100px; + margin-bottom: 0.5em; } .home-logo svg { width: 150px; @@ -221,6 +223,9 @@ button:hover .icon svg { box-shadow: 0 0 10px 0 $drop-shadow; box-sizing: border-box; } +.modal-content .table-of-contents { + padding: 2em; +} .closed { display: none; } diff --git a/assets/templates/_header.phtml b/assets/templates/_header.phtml index 0602f31..c8a716d 100644 --- a/assets/templates/_header.phtml +++ b/assets/templates/_header.phtml @@ -6,6 +6,7 @@ <?= $title ?? 'PHP Apprentice' ?> + diff --git a/assets/templates/credits.phtml b/assets/templates/credits.phtml index 730715a..3e48fe4 100644 --- a/assets/templates/credits.phtml +++ b/assets/templates/credits.phtml @@ -1,40 +1,51 @@ 'PHP Apprentice - Credits']) ?> -
-
-
-

Credits

-

- PHP Apprentice was inspired by - Go By Example and by Elixir School. Both sites offer excellent, quality documentation in Go and Elixir and I want PHP Apprentice to provide the same - experience for the PHP programming language. -

-

- Sites used as a reference while writing PHP Apprentice: -

-

-

- PHP Apprentice was built using several open source projects, besides PHP itself. - Thank you to each of these maintainers for providing great libraries! -

-

-
-

- phpapprentice.com was created and is managed by Andrew Davis. -

-
-
- -
+
+ + +
+

Credits

+

+ PHP Apprentice was inspired by + Go By Example and by Elixir School. Both sites offer excellent, quality documentation in Go and Elixir and I want PHP Apprentice to provide the same + experience for the PHP programming language. +

+

+ Sites used as a reference while writing PHP Apprentice: +

+

+

+ PHP Apprentice was built using several open source projects, besides PHP itself. + Thank you to each of these maintainers for providing great libraries! +

+

+
+

+ phpapprentice.com was created and is managed by Andrew Davis. +

+
+
+ + diff --git a/assets/templates/default.phtml b/assets/templates/default.phtml index 77a22bd..f62c0d2 100644 --- a/assets/templates/default.phtml +++ b/assets/templates/default.phtml @@ -1,6 +1,6 @@ $title, 'subtitle' => $subtitle]) ?> -
+
+
+
+ +

PHP Apprentice

+

A site for learning how to use PHP

+

+ The goal of PHP Apprentice is to be an easy to understand resource for learning how to write good code in the PHP programming language. There are a lot of PHP tutorials on the internet that use outdated libraries, insecure programming practices or inefficient code. I want this site to show how to write PHP code with quality. +

+

+ The site currently has content for learning the basics of PHP. In the future, more pages will be added for more advanced topics like building websites, database integration and security. +

+

+ PHP Apprentice is currently a work in progress. If you would like to contribute or request a certain discussion topic, check out the GitHub repository. +

+

+ To get started, you will need to install PHP, have a text editor and open your terminal. + Each example in PHP Apprentice can by typed into a PHP file and executed in the terminal. + Let's get started! 😃 +

+ +
+ Open First Chapter +
+
+
+ + diff --git a/assets/templates/installing-php.phtml b/assets/templates/installing-php.phtml index 7d86fc5..2187771 100644 --- a/assets/templates/installing-php.phtml +++ b/assets/templates/installing-php.phtml @@ -1,30 +1,41 @@ 'PHP Apprentice - Installing PHP']) ?> -
-
-
-

Installing PHP

-

Windows 10

-
    -
  1. Download PHP from the PHP For Windows site. I recommend downloading the non-thread safe PHP 7.1 or 7.2 (unless you want to use Apache and you know if you do). You will also need to choose 32 bit (x86) or 64 bit (x64) depending on your version of Windows.
  2. -
  3. Download the corresponding Visual C++ Redistributable from the same site. For PHP 7.1, it is VC14 and for PHP 7.2, it is VC15.
  4. -
  5. Install the VC redistrubutable using the downloaded executable.
  6. -
  7. The PHP download comes in a zip folder. Extract the zip folder into your user directory in a folder named "php". The path should look like "C:\Users\username\php".
  8. -
  9. Next, we need to add PHP to your environment variables path. Open "System" under "Control Panel". Go to the "Advanced" tab and select "Environment Variables". In the "User variables" section, select "Path" and click the "Edit" button. You will see a list of different folder paths. Add the PHP path to the list using the "Add" button. The PHP path is the same folder where you extracted PHP.
  10. -
  11. Now, you can open PowerShell or Command Prompt and type "php -v" to verify PHP was installed correctly. It will return the installed version of PHP on your system.
  12. -
+
+ -

MacOS

-

Good news! Each version of MacOS comes with PHP by default. However, if you are running an older version of MacOS or OS X, then you will need to manually install a new version of PHP. To check your current version, open Terminal and type "php -v". You will need at least PHP 7.1 on your computer to use all the features in these tutorials.

-
    -
  1. Install Homebrew.
  2. -
  3. Run "brew install php" in Terminal.
  4. -
  5. Check your version is correct by running "php -v" in Terminal.
  6. -
-
-
- -
+
+

Installing PHP

+

Windows 10

+
    +
  1. Download PHP from the PHP For Windows site. I recommend downloading the non-thread safe PHP 7.1 or 7.2 (unless you want to use Apache and you know if you do). You will also need to choose 32 bit (x86) or 64 bit (x64) depending on your version of Windows.
  2. +
  3. Download the corresponding Visual C++ Redistributable from the same site. For PHP 7.1, it is VC14 and for PHP 7.2, it is VC15.
  4. +
  5. Install the VC redistrubutable using the downloaded executable.
  6. +
  7. The PHP download comes in a zip folder. Extract the zip folder into your user directory in a folder named "php". The path should look like "C:\Users\username\php".
  8. +
  9. Next, we need to add PHP to your environment variables path. Open "System" under "Control Panel". Go to the "Advanced" tab and select "Environment Variables". In the "User variables" section, select "Path" and click the "Edit" button. You will see a list of different folder paths. Add the PHP path to the list using the "Add" button. The PHP path is the same folder where you extracted PHP.
  10. +
  11. Now, you can open PowerShell or Command Prompt and type "php -v" to verify PHP was installed correctly. It will return the installed version of PHP on your system.
  12. +
+ +

MacOS

+

Good news! Each version of MacOS comes with PHP by default. However, if you are running an older version of MacOS or OS X, then you will need to manually install a new version of PHP. To check your current version, open Terminal and type "php -v". You will need at least PHP 7.1 on your computer to use all the features in these tutorials.

+
    +
  1. Install Homebrew.
  2. +
  3. Run "brew install php" in Terminal.
  4. +
  5. Check your version is correct by running "php -v" in Terminal.
  6. +
+
+
+ + diff --git a/chapters/abstract.md b/chapters/abstract.md new file mode 100644 index 0000000..eda14a6 --- /dev/null +++ b/chapters/abstract.md @@ -0,0 +1,58 @@ +Abstract classes are similar to interfaces in that they define methods that a sub-class must implement. +However, an abstract class can also have normal methods. To create an abstract class, use the "abstract" +keyword followed by class and the name of the class. +```php +turnOn(); +$iPhone->unlock(); + +$android = new Android(); +$android->turnOn(); +$android->unlock(); +``` + +Lastly, you cannot create an instance of an abstract class. PHP would not know how to use the abstract methods +so when you try to create an abstract instance you will get an error. +```php +$cellPhone = new CellPhone(); +``` diff --git a/chapters/arithmetic.md b/chapters/arithmetic.md new file mode 100644 index 0000000..43a3430 --- /dev/null +++ b/chapters/arithmetic.md @@ -0,0 +1,40 @@ +Now that we know how to create variables, let's look at doing some math. +```php + 'Toyota', 'model' => 'Camry']; +``` + +To access the value in an associative array, just use the string key in brackets +after the variable name. +```php +echo $car['model'] . "\n"; +``` diff --git a/chapters/basics.md b/chapters/basics.md new file mode 100644 index 0000000..2ac6b16 --- /dev/null +++ b/chapters/basics.md @@ -0,0 +1,33 @@ + +In the tradition of our ancestors, let's start with a hello world program. +All PHP files must start with a color = $color; + } +} + +$hat = new Hat(); +``` + +However, you can actually pass data into the parentheses like a function. +The data will be passed to a special function on the class called a constructor. +```php +class Ballcap +{ + public $color; + + public function __construct($color) + { + $this->color = $color; + } +} +``` + +A constructor is not required, but can make creating a new object easier. +They are usually used to define the initial value of a property. +Instead of writing: +```php +$hat = new Hat(); +$hat->setColor('Red'); +``` + +You can write: +```php +$ballcap = new Ballcap('Blue'); +``` + +Constructors do not return values because the return value is a always a new object. +```php +class Tophat +{ + public function __construct($color) + { + return $color; + } +} +``` + +"$tophat" now holds an instance of Tophat, not the color "Grey". +```php +$tophat = new Tophat('Grey'); +``` diff --git a/chapters/classes-inheritance.md b/chapters/classes-inheritance.md new file mode 100644 index 0000000..6cb9530 --- /dev/null +++ b/chapters/classes-inheritance.md @@ -0,0 +1,78 @@ +In PHP, a class can extend another class, inheriting the parent class' +properties and methods. To make a class a child of another, use the "extends" +keyword after the class name. +```php +drive(); +``` + +Even though the child class inherits a parent class' properties and methods, +the child can still override the parent. +```php +class Tractor extends Vehicle +{ + public function drive() + { + echo "driving slowly...\n"; + } +} +``` + +The drive function now outputs "driving slowly..." instead of "driving...". +```php +$tractor = new Tractor(); +$tractor->drive(); +``` + +A class can use a parent's property or method from the "$this" variable. +```php +class Motorcycle extends Vehicle +{ + public function pushPedal() + { + $this->drive(); + } +} +``` + +Outputs "driving...". +```php +$cycle = new Motorcycle(); +$cycle->pushPedal(); +``` + +If you override a parent's property or method, the "$this" variable will refer to the child's +implementation of the property or method. To call the parent's property or method explicity, +use the "parent" keyword. +```php +class Racecar extends Vehicle +{ + public function drive() + { + parent::drive(); + + echo "driving even faster...\n"; + } +} +``` + +Outputs "driving..." and "driving even faster...". +```php +$racecar = new Racecar(); +$racecar->drive(); +``` diff --git a/chapters/classes-visibility.md b/chapters/classes-visibility.md new file mode 100644 index 0000000..f24524d --- /dev/null +++ b/chapters/classes-visibility.md @@ -0,0 +1,70 @@ +In the last chapter, we defined properties and methods on the class using the public keyword. +You can also define them using the "protected" and "private" keywords. +Both keywords prevent the properties and functions from being accessible outside the object. +Only the object itself can use each. +```php +number = $number; + } +} +``` + +We cannot set the number using "$phone->number = '123-456-7890'". +Instead, we can use the public method. +```php +$phone = new Phone(); +$phone->setNumber('123-456-7890'); +``` + +Making an attribute or function private, gives you more control over the data in the object. +For example, we could prevent a number being set if it starts with a 7. +```php +class Phone2 +{ + private $number; + + public function setNumber($number) + { + if (substr($number, 0, 1) !== '7') { + $this->number = $number; + } + } +} +``` + +The "protected" and "private" keywords work a little differently. +They both prevent functions and properties from being accessed outside an object. +However, a method or property marked "protected" can still be accessed by a child class. +```php +class Phone3 +{ + private $number; + + protected $caller; + + public function setNumber($number) + { + $this->number = $number; + } +} +``` + +In class "Smartphone", the "caller" property is accessible because the parent class +has it marked as "protected". However, "Smartphone" cannot access the "number" property +because it is still listed as private. +```php +class Smartphone extends Phone3 +{ + public function setCaller($caller) + { + $this->caller = $caller; + } +} +``` diff --git a/chapters/classes.md b/chapters/classes.md new file mode 100644 index 0000000..413c688 --- /dev/null +++ b/chapters/classes.md @@ -0,0 +1,64 @@ + +Classes allow you to define your own data types. All classes start with the +class keyword followed by the name of the class and opening and closing curly braces. +```php +". +```php +$bike = new Bicycle(); +$bike->color = 'Blue'; +echo $bike->color . "\n"; +``` + +An instance of a class is called an object. Congratulations! +You are now performing object-oriented development. +```php +$redBike = new Bicycle(); +$redBike->color = 'Red'; +echo $redBike->color . " Bike Object\n"; +``` + +A method is a function attached to the class. You can add a method +to a class by using the "public" keyword followed by the function. A method +can access the attributes and methods of an object instance using the "$this" variable. +```php +class Tricycle +{ + public $color; + + public function echoColor() + { + echo $this->color . "\n"; + } +} +``` + +You can execute a method on an object using the same "->" arrow characters. +```php +$bike = new Tricycle(); +$bike->color = 'Red'; +$bike->echoColor(); +``` diff --git a/chapters/comparisons.md b/chapters/comparisons.md new file mode 100644 index 0000000..dfb45cf --- /dev/null +++ b/chapters/comparisons.md @@ -0,0 +1,65 @@ + +A boolean is a value that is always 0 or 1, yes or no, on or off. +In PHP, a boolean is represented by the words true and false. +While programming, you will often want to know if something is positive or negative. +```php + $two; +``` + +This statement will return true. +```php +$one < $two; +``` + +If you combine a greater than or less than symbol with an equal, +it will check if the value is greater or less than or equal to another value. +```php +$one <= $two; +$one >= $two; +``` +You can also check that two values are equal and of the same type +by using three equal signs. + +This returns true. +```php +1 == 1; +1 == '1'; +1 == true; +1 == 1.0; +1 === 1; +``` + +This returns false. +```php +1 === '1'; +1 === true; +1 === 1.0; +``` diff --git a/chapters/conditionals.md b/chapters/conditionals.md new file mode 100644 index 0000000..712cf04 --- /dev/null +++ b/chapters/conditionals.md @@ -0,0 +1,84 @@ + +When writing code, there will be times when you need to perform actions only under certain circumstances. +There are several ways to control execution in PHP. +We will start with an if statement. +```php +getNumber()) !== 16) { + throw new Exception('Credit card is not right'); + } + } +} +``` + +In this case, if someone tried to use the Processor class +to charge a credit card number that is not 16 characters long, an +exception will be thrown which stops the rest of the code from running. +```php +$processor = new Processor(); +$processor->charge('1234'); +``` + +A developer who wants to prevent an exception from stopping code execution +can catch the exception and use it for logging or display the error to a user. + +Just wrap the code that might throw an exception with the keyword "try" and brackets +followed by "catch", the exception type in parentheses and more brackets. +```php +try { + $processor->charge('1234'); +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} +``` + +You can make your own custom exceptions as well. They are just classes +that extend Exception. +```php +class MyCustomException extends Exception {} +``` + +Then, you can try to catch your exception instead of the base exception. +```php +try { + throw new MyCustomException('I am a custom exception'); +} catch (MyCustomException $e) { + echo "Cauth MyCustomException\n"; +} +``` + +Since all exceptions inherit from Exception, catching +Exception will catch any and all exceptions that might be thrown. +```php +try { + throw new MyCustomException('I inherit from Exception'); +} catch (Exception $e) { + echo "Still caught MyCustomException\n"; +} +``` + +You can also create multiple catch blocks to handle different types of exceptions in +one try-catch. +```php +try { + throw new MyCustomException('I am being thrown again'); +} catch (MyCustomException $e) { + echo "MyCustomException was caught\n"; +} catch (Exception $e) { + echo "Just in case a different exception is thrown\n"; +} +``` diff --git a/chapters/functions.md b/chapters/functions.md new file mode 100644 index 0000000..6aaa8ac --- /dev/null +++ b/chapters/functions.md @@ -0,0 +1,62 @@ +A function allows you to store code under a name and then execute +that code later. + +A function always starts with the +function keyword followed by the name with parentheses and then +opening and closing curly braces around the code. +```php +color = $color; + } + + public function setLegs($number) + { + $this->legs = $number; + } +} +``` + +Interfaces are helpful when you are using code created by someone else. +For example, another developer may have created code that manages online payments, but they want to give you +the ability to create your own payment class that works with their code. In that case, the developer +creates an interface with all the required methods they need to charge a payment. The interface +becomes a contract between your code and the other developer's code to work a certain way. +```php +interface Payment +{ + public function charge($amount); +} + +class CreditCard +{ + public function charge($amount) + { + + } +} +``` + +Since CreditCard implements Payment, other developers can use the charge method, knowing it exists on the class. +```php +$creditCard = new CreditCard(); +$creditCard->charge(25); +``` diff --git a/chapters/loops.md b/chapters/loops.md new file mode 100644 index 0000000..b4eabfc --- /dev/null +++ b/chapters/loops.md @@ -0,0 +1,69 @@ +A loop tells PHP to run a block of code more than once. +A classic loop is a while loop. +A "while" loop will continue to run the block of code as long as the value in parentheses is true. +```php + 0) { + echo "While loop $num\n"; + --$num; +} +``` + +A "do while" loop is similar to a "while" loop except it always runs at least +one iteration. In a classic "while" loop, no iterations may be executed if +the value in parentheses is false. In a "do while", the boolean check +is not done until after the execution of an iteration. +```php +$num = 0; +do { + echo "Do while $num\n"; + ++$num; +} while ($num < 5); +``` + +"for" loops allow you to create a more concise while loop. +Inside the parentheses, the left section creates a variable before the loop +starts, the middle section is the check that is done at the beginning of each loop +and the third section is executed after each loop. +```php +for ($i = 0; $i < 10; $i++) { + echo "For loop $i\n"; +} +``` + +A "foreach" loop allows you to easily loop over an array. +An array is a list of data stored together. +The "as" keyword lets you assign a variable to the value +in the array for the current iteration of the loop. +```php +$set = [1, 2, 3, 4, 5]; +foreach ($set as $num) { + echo "Array value $num\n"; +} +``` + +In loops, you can use the keyword "break" to stop the loop execution +no matter how many more iterations should run. +```php +$values = ['one', 'two', 'three']; +foreach ($values as $value) { + if ($value === 'two') { + break; + } + echo "Break $value\n"; +} +``` + +The "continue" keyword stops executing the current loop iteration, +but then allows the loop to continue with other iterations. +```php +$values = ['one', 'skip', 'three']; +foreach ($values as $value) { + if ($value === 'skip') { + continue; + } + echo "Continue $value\n"; +} +``` diff --git a/chapters/static.md b/chapters/static.md new file mode 100644 index 0000000..3afdfc7 --- /dev/null +++ b/chapters/static.md @@ -0,0 +1,81 @@ +When writing a class, all of the properties and methods are being defined for the object +that will be created from the class. +```php +color = $color; + } +} +``` + +Like building a house, a class is a blueprint that +defines what the house can do and the object is the house itself that can actually +perform the actions defined in the blueprint. +```php +$house = new House('Green'); +``` + +However, what if you want the blueprint to have properties and methods? +That is when you use the "static" keyword. In this class, we will define a default color +on the class itself and then use it when creating a new object. +```php +class Skyscraper +{ + private static $popularColor; + public $color; + + public static function setDefaultColor($color) + { + self::$popularColor = $color; + } + + public function __construct() + { + $this->color = self::$popularColor; + } +} +``` + +You can access static methods and properties using double colons on "self" inside the object +or on the class name outside of the object. Static methods and properties can only access +other static methods and properties. +```php +Skyscraper::setDefaultColor('Grey'); +$skyscraper = new Skyscraper(); +echo $skyscraper->color . "\n"; +``` + +Often, you will see static constructors in PHP. +A static constructor creates a new instance of an object. Why would do that when you can just use "new Class" to create +the object? The most common reason is to make the code more readable. +```php +class TinyHouse +{ + private $color; + private $wheels; + private $trailer; + + public static function build($color, $wheels, $trailer) + { + return new self($color, $wheels, $trailer); + } + + public function __construct($color, $wheels, $trailer) + { + $this->color = $color; + $this->wheels = $wheels; + $this->trailer = $trailer; + } +} +``` + +Using "build" can make more sense than "new", but it is ultimately a personal preference. +```php +$house = TinyHouse::build('Blue', 4, true); +``` diff --git a/chapters/strings.md b/chapters/strings.md new file mode 100644 index 0000000..cdc70b7 --- /dev/null +++ b/chapters/strings.md @@ -0,0 +1,31 @@ +As seen in the first chapter, a string is a group of characters created by +surrounding text in single or double quotes. +```php +turnOn(); -$iPhone->unlock(); - -$android = new Android(); -$android->turnOn(); -$android->unlock(); - -// Lastly, you cannot create an instance of an abstract class. PHP would not know how to use the abstract methods -// so when you try to create an abstract instance you will get an error. -$cellPhone = new CellPhone(); diff --git a/code/arithmetic.php b/code/arithmetic.php deleted file mode 100644 index 57fb4da..0000000 --- a/code/arithmetic.php +++ /dev/null @@ -1,26 +0,0 @@ - 'Toyota', 'model' => 'Camry']; - -// To access the value in an associative array, just use the string key in brackets -// after the variable name. -echo $car['model'] . "\n"; diff --git a/code/basics.php b/code/basics.php deleted file mode 100644 index e62abbd..0000000 --- a/code/basics.php +++ /dev/null @@ -1,23 +0,0 @@ -color = $color; - } -} - -$hat = new Hat(); - -// However, you can actually pass data into the parentheses like a function. -// The data will be passed to a special function on the class called a constructor. -class Ballcap -{ - public $color; - - public function __construct($color) - { - $this->color = $color; - } -} - -// A constructor is not required, but can make creating a new object easier. -// They are usually used to define the initial value of a property. -// Instead of writing: -$hat = new Hat(); -$hat->setColor('Red'); - -// You can write: -$ballcap = new Ballcap('Blue'); - -// Constructors do not return values because the return value is a always a new object. -class Tophat -{ - public function __construct($color) - { - return $color; - } -} - -// "$tophat" now holds an instance of Tophat, not the color "Grey". -$tophat = new Tophat('Grey'); diff --git a/code/classes-inheritance.php b/code/classes-inheritance.php deleted file mode 100644 index 2bbb2cd..0000000 --- a/code/classes-inheritance.php +++ /dev/null @@ -1,62 +0,0 @@ -drive(); - -// Even though the child class inherits a parent class' properties and methods, -// the child can still override the parent. -class Tractor extends Vehicle -{ - public function drive() - { - echo "driving slowly...\n"; - } -} - -// The drive function now outputs "driving slowly..." instead of "driving...". -$tractor = new Tractor(); -$tractor->drive(); - -// A class can use a parent's property or method from the "$this" variable. -class Motorcycle extends Vehicle -{ - public function pushPedal() - { - $this->drive(); - } -} - -// Outputs "driving...". -$cycle = new Motorcycle(); -$cycle->pushPedal(); - -// If you override a parent's property or method, the "$this" variable will refer to the child's -// implementation of the property or method. To call the parent's property or method explicity, -// use the "parent" keyword. -class Racecar extends Vehicle -{ - public function drive() - { - parent::drive(); - - echo "driving even faster...\n"; - } -} - -// Outputs "driving..." and "driving even faster...". -$racecar = new Racecar(); -$racecar->drive(); diff --git a/code/classes-visibility.php b/code/classes-visibility.php deleted file mode 100644 index 509e89b..0000000 --- a/code/classes-visibility.php +++ /dev/null @@ -1,60 +0,0 @@ -number = $number; - } -} - -// We cannot set the number using "$phone->number = '123-456-7890'". -// Instead, we can use the public method. -$phone = new Phone(); -$phone->setNumber('123-456-7890'); - -// Making an attribute or function private, gives you more control over the data in the object. -// For example, we could prevent a number being set if it starts with a 7. -class Phone2 -{ - private $number; - - public function setNumber($number) - { - if (substr($number, 0, 1) !== '7') { - $this->number = $number; - } - } -} - -// The "protected" and "private" keywords work a little differently. -// They both prevent functions and properties from being accessed outside an object. -// However, a method or property marked "protected" can still be accessed by a child class. -class Phone3 -{ - private $number; - - protected $caller; - - public function setNumber($number) - { - $this->number = $number; - } -} - -// In class "Smartphone", the "caller" property is accessible because the parent class -// has it marked as "protected". However, "Smartphone" cannot access the "number" property -// because it is still listed as private. -class Smartphone extends Phone3 -{ - public function setCaller($caller) - { - $this->caller = $caller; - } -} diff --git a/code/classes.php b/code/classes.php deleted file mode 100644 index 039d816..0000000 --- a/code/classes.php +++ /dev/null @@ -1,49 +0,0 @@ -". -$bike = new Bicycle(); -$bike->color = 'Blue'; -echo $bike->color . "\n"; - -// An instance of a class is called an object. Congratulations! -// You are now performing object-oriented development. -$redBike = new Bicycle(); -$redBike->color = 'Red'; -echo $redBike->color . " Bike Object\n"; - -// A method is a function attached to the class. You can add a method -// to a class by using the "public" keyword followed by the function. A method -// can access the attributes and methods of an object instance using the "$this" variable. -class Tricycle -{ - public $color; - - public function echoColor() - { - echo $this->color . "\n"; - } -} - -// You can execute a method on an object using the same "->" arrow characters. -$bike = new Tricycle(); -$bike->color = 'Red'; -$bike->echoColor(); diff --git a/code/comparisons.php b/code/comparisons.php deleted file mode 100644 index 444c853..0000000 --- a/code/comparisons.php +++ /dev/null @@ -1,47 +0,0 @@ - $two; - -// This statement will return true. -$one < $two; - -// If you combine a greater than or less than symbol with an equal, -// it will check if the value is greater or less than or equal to another value. -$one <= $two; -$one >= $two; - -// You can also check that two values are equal and of the same type -// by using three equal signs. - -// This returns true. -1 == 1; -1 == '1'; -1 == true; -1 == 1.0; -1 === 1; - -// This returns false. -1 === '1'; -1 === true; -1 === 1.0; diff --git a/code/conditionals.php b/code/conditionals.php deleted file mode 100644 index d2b8d17..0000000 --- a/code/conditionals.php +++ /dev/null @@ -1,70 +0,0 @@ -getNumber()) !== 16) { - throw new Exception('Credit card is not right'); - } - } -} - -// In this case, if someone tried to use the Processor class -// to charge a credit card number that is not 16 characters long, an -// exception will be thrown which stops the rest of the code from running. -$processor = new Processor(); -$processor->charge('1234'); - -// A developer who wants to prevent an exception from stopping code execution -// can catch the exception and use it for logging or display the error to a user. - -// Just wrap the code that might throw an exception with the keyword "try" and brackets -// followed by "catch", the exception type in parentheses and more brackets. -try { - $processor->charge('1234'); -} catch (Exception $e) { - echo $e->getMessage() . "\n"; -} - -// You can make your own custom exceptions as well. They are just classes -// that extend Exception. -class MyCustomException extends Exception {} - -// Then, you can try to catch your exception instead of the base exception. -try { - throw new MyCustomException('I am a custom exception'); -} catch (MyCustomException $e) { - echo "Cauth MyCustomException\n"; -} - -// Since all exceptions inherit from Exception, catching -// Exception will catch any and all exceptions that might be thrown. -try { - throw new MyCustomException('I inherit from Exception'); -} catch (Exception $e) { - echo "Still caught MyCustomException\n"; -} - -// You can also create multiple catch blocks to handle different types of exceptions in -// one try-catch. -try { - throw new MyCustomException('I am being thrown again'); -} catch (MyCustomException $e) { - echo "MyCustomException was caught\n"; -} catch (Exception $e) { - echo "Just in case a different exception is thrown\n"; -} diff --git a/code/functions.php b/code/functions.php deleted file mode 100644 index d610f12..0000000 --- a/code/functions.php +++ /dev/null @@ -1,46 +0,0 @@ -color = $color; - } - - public function setLegs($number) - { - $this->legs = $number; - } -} - -// Interfaces are helpful when you are using code created by someone else. -// For example, another developer may have created code that manages online payments, but they want to give you -// the ability to create your own payment class that works with their code. In that case, the developer -// creates an interface with all the required methods they need to charge a payment. The interface -// becomes a contract between your code and the other developer's code to work a certain way. -interface Payment -{ - public function charge($amount); -} - -class CreditCard -{ - public function charge($amount) - { - - } -} - -// Since CreditCard implements Payment, other developers can use the charge method, knowing it exists on the class. -$creditCard = new CreditCard(); -$creditCard->charge(25); diff --git a/code/loops.php b/code/loops.php deleted file mode 100644 index 9916b83..0000000 --- a/code/loops.php +++ /dev/null @@ -1,57 +0,0 @@ - 0) { - echo "While loop $num\n"; - --$num; -} - -// A "do while" loop is similar to a "while" loop except it always runs at least -// one iteration. In a classic "while" loop, no iterations may be executed if -// the value in parentheses is false. In a "do while", the boolean check -// is not done until after the execution of an iteration. -$num = 0; -do { - echo "Do while $num\n"; - ++$num; -} while ($num < 5); - -// "for" loops allow you to create a more concise while loop. -// Inside the parentheses, the left section creates a variable before the loop -// starts, the middle section is the check that is done at the beginning of each loop -// and the third section is executed after each loop. -for ($i = 0; $i < 10; $i++) { - echo "For loop $i\n"; -} - -// A "foreach" loop allows you to easily loop over an array. -// An array is a list of data stored together. -// The "as" keyword lets you assign a variable to the value -// in the array for the current iteration of the loop. -$set = [1, 2, 3, 4, 5]; -foreach ($set as $num) { - echo "Array value $num\n"; -} - -// In loops, you can use the keyword "break" to stop the loop execution -// no matter how many more iterations should run. -$values = ['one', 'two', 'three']; -foreach ($values as $value) { - if ($value === 'two') { - break; - } - echo "Break $value\n"; -} - -// The "continue" keyword stops executing the current loop iteration, -// but then allows the loop to continue with other iterations. -$values = ['one', 'skip', 'three']; -foreach ($values as $value) { - if ($value === 'skip') { - continue; - } - echo "Continue $value\n"; -} diff --git a/code/static.php b/code/static.php deleted file mode 100644 index ceba59f..0000000 --- a/code/static.php +++ /dev/null @@ -1,70 +0,0 @@ -color = $color; - } -} - -// Like building a house, a class is a blueprint that -// defines what the house can do and the object is the house itself that can actually -// perform the actions defined in the blueprint. -$house = new House('Green'); - -// However, what if you want the blueprint to have properties and methods? -// That is when you use the "static" keyword. In this class, we will define a default color -// on the class itself and then use it when creating a new object. -class Skyscraper -{ - private static $popularColor; - public $color; - - public static function setDefaultColor($color) - { - self::$popularColor = $color; - } - - public function __construct() - { - $this->color = self::$popularColor; - } -} - -// You can access static methods and properties using double colons on "self" inside the object -// or on the class name outside of the object. Static methods and properties can only access -// other static methods and properties. -Skyscraper::setDefaultColor('Grey'); -$skyscraper = new Skyscraper(); -echo $skyscraper->color . "\n"; - -// Often, you will see static constructors in PHP. -// A static constructor creates a new instance of an object. Why would do that when you can just use "new Class" to create -// the object? The most common reason is to make the code more readable. -class TinyHouse -{ - private $color; - private $wheels; - private $trailer; - - public static function build($color, $wheels, $trailer) - { - return new self($color, $wheels, $trailer); - } - - public function __construct($color, $wheels, $trailer) - { - $this->color = $color; - $this->wheels = $wheels; - $this->trailer = $trailer; - } -} - -// Using "build" can make more sense than "new", but it is ultimately a personal preference. -$house = TinyHouse::build('Blue', 4, true); diff --git a/code/strings.php b/code/strings.php deleted file mode 100644 index 677b503..0000000 --- a/code/strings.php +++ /dev/null @@ -1,23 +0,0 @@ -=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35" + }, + "type": "library", + "autoload": { + "psr-0": { + "Parsedown": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", + "keywords": [ + "markdown", + "parser" + ], + "time": "2018-03-08T01:11:30+00:00" + }, { "name": "symfony/console", "version": "v4.1.4", diff --git a/config.php b/config.php index eeabd3d..d764519 100644 --- a/config.php +++ b/config.php @@ -12,10 +12,10 @@ return [ /* * - * Directory holding code files used in examples + * Directory holding chapter files * */ - 'code_dir' => __DIR__ . '/code', + 'chapter_dir' => __DIR__ . '/chapters', /* * @@ -48,108 +48,108 @@ return [ Page::create('installing-php', 'installing-php.phtml'), Page::create('credits', 'credits.phtml'), Page::create('404', '404.phtml'), - Page::create('basics', null, 'basics.php', [ + Page::create('basics', null, 'basics.md', [ 'title' => 'Basics', 'subtitle' => 'Getting started', 'next' => 'variables', ]), - Page::create('variables', null, 'variables.php', [ + Page::create('variables', null, 'variables.md', [ 'title' => 'Variables', 'subtitle' => 'The building blocks of PHP', 'previous' => 'basics', 'next' => 'arithmetic', ]), - Page::create('arithmetic', null, 'arithmetic.php', [ + Page::create('arithmetic', null, 'arithmetic.md', [ 'title' => 'Arithmetic', 'subtitle' => 'Doing math like a pro', 'previous' => 'variables', 'next' => 'strings', ]), - Page::create('strings', null, 'strings.php', [ + Page::create('strings', null, 'strings.md', [ 'title' => 'Strings', 'subtitle' => 'Working with text', 'previous' => 'arithmetic', 'next' => 'comparisons', ]), - Page::create('comparisons', null, 'comparisons.php', [ + Page::create('comparisons', null, 'comparisons.md', [ 'title' => 'Comparisons', 'subtitle' => 'Equality checking', 'previous' => 'strings', 'next' => 'boolean-logic', ]), - Page::create('boolean-logic', null, 'boolean-logic.php', [ + Page::create('boolean-logic', null, 'boolean-logic.md', [ 'title' => 'Boolean Logic', 'subtitle' => 'Is it a yes or a no?', 'previous' => 'comparisons', 'next' => 'conditionals', ]), - Page::create('conditionals', null, 'conditionals.php', [ + Page::create('conditionals', null, 'conditionals.md', [ 'title' => 'Conditionals', 'subtitle' => 'Checking the if before the what', 'previous' => 'boolean-logic', 'next' => 'loops', ]), - Page::create('loops', null, 'loops.php', [ + Page::create('loops', null, 'loops.md', [ 'title' => 'Loops', 'subtitle' => 'Increase your repetitions', 'previous' => 'conditionals', 'next' => 'arrays', ]), - Page::create('arrays', null, 'arrays.php', [ + Page::create('arrays', null, 'arrays.md', [ 'title' => 'Arrays', 'subtitle' => 'Time to make a list', 'previous' => 'loops', 'next' => 'functions', ]), - Page::create('functions', null, 'functions.php', [ + Page::create('functions', null, 'functions.md', [ 'title' => 'Functions', 'subtitle' => 'Reusable code', 'previous' => 'arrays', 'next' => 'classes', ]), - Page::create('classes', null, 'classes.php', [ + Page::create('classes', null, 'classes.md', [ 'title' => 'Classes', 'subtitle' => 'Object-oriented programming', 'previous' => 'functions', 'next' => 'classes-inheritance', ]), - Page::create('classes-inheritance', null, 'classes-inheritance.php', [ + Page::create('classes-inheritance', null, 'classes-inheritance.md', [ 'title' => 'Classes: Inheritance', 'subtitle' => 'Extend your objects', 'previous' => 'classes', 'next' => 'classes-visibility', ]), - Page::create('classes-visibility', null, 'classes-visibility.php', [ + Page::create('classes-visibility', null, 'classes-visibility.md', [ 'title' => 'Classes: Visibility', 'subtitle' => 'Privatizing your objects', 'previous' => 'classes-inheritance', 'next' => 'classes-constructor', ]), - Page::create('classes-constructor', null, 'classes-constructor.php', [ + Page::create('classes-constructor', null, 'classes-constructor.md', [ 'title' => 'Classes: Constructor', 'subtitle' => 'Construct your objects', 'previous' => 'classes-visibility', 'next' => 'static', ]), - Page::create('static', null, 'static.php', [ + Page::create('static', null, 'static.md', [ 'title' => 'Static', 'subtitle' => 'Class properties and methods', 'previous' => 'classes-constructor', 'next' => 'interfaces', ]), - Page::create('interfaces', null, 'interfaces.php', [ + Page::create('interfaces', null, 'interfaces.md', [ 'title' => 'Interfaces', 'subtitle' => 'Writing code contracts', 'previous' => 'static', 'next' => 'abstract', ]), - Page::create('abstract', null, 'abstract.php', [ + Page::create('abstract', null, 'abstract.md', [ 'title' => 'Abstract Classes', 'subtitle' => 'Inheriting an interface', 'previous' => 'interfaces', 'next' => 'exceptions', ]), - Page::create('exceptions', null, 'exceptions.php', [ + Page::create('exceptions', null, 'exceptions.md', [ 'title' => 'Exceptions', 'subtitle' => 'Throwing errors', 'previous' => 'abstract', diff --git a/src/Build.php b/src/Build.php index e239914..3d2e2ed 100644 --- a/src/Build.php +++ b/src/Build.php @@ -2,6 +2,8 @@ namespace Apprentice; +use Parsedown; + /** * Handles building pages in public folder * using configuration based on Page classes @@ -95,13 +97,15 @@ class Build */ private function buildPage(Page $page): string { - if (!empty($page->code)) { - if (!file_exists(config('code_dir') . '/' . $page->code)) { - throw new \Exception('Code file not found: ' . $page->code); + if (!empty($page->chapter)) { + if (!file_exists(config('chapter_dir') . '/' . $page->chapter)) { + throw new \Exception('Code file not found: ' . $page->chapter); } - $code = file_get_contents(config('code_dir') . '/' . $page->code); - $page->variables['code'] = $code; + $parser = new Parsedown(); + + $content = file_get_contents(config('chapter_dir') . '/' . $page->chapter); + $page->variables['chapter'] = $parser->text($content); } if ($page->template) { diff --git a/src/Page.php b/src/Page.php index 2ccf0db..f2689f8 100644 --- a/src/Page.php +++ b/src/Page.php @@ -23,12 +23,12 @@ class Page public $template; /** - * Name of code file to load as table + * Name of chapter file to load and parse * Will be skipped if none is provided * * @var string|null */ - public $code; + public $chapter; /** * Map of data to passed to template @@ -42,18 +42,18 @@ class Page * * @param string $name * @param string|null $template - * @param string|null $code + * @param string|null $chapter * @param array $variables */ public function __construct( string $name, ?string $template = null, - ?string $code = null, + ?string $chapter = null, ?array $variables = [] ) { $this->name = $name; $this->template = $template; - $this->code = $code; + $this->chapter = $chapter; $this->variables = $variables; } @@ -69,9 +69,9 @@ class Page public static function create( string $name, ?string $template = null, - ?string $code = null, + ?string $chapter = null, ?array $variables = [] ): Page { - return new static($name, $template, $code, $variables); + return new static($name, $template, $chapter, $variables); } } diff --git a/src/util/functions.php b/src/util/functions.php index ee93b74..f0682f1 100644 --- a/src/util/functions.php +++ b/src/util/functions.php @@ -61,65 +61,6 @@ function icon(string $name): string { return ''; } -/** - * Takes string of PHP code and converts it into html for display - * - * @param string $code - * @return string - */ -function code_table(string $code): string { - $tokens = token_get_all($code); - $output = '
' . - '
' . - '
' . - '
' .
-                        '';
-
-    $previousTokenWasComment = false;
-    foreach ($tokens as $token) {
-        if (is_string($token)) {
-            $output .= $token;
-            continue;
-        }
-
-        $id = $token[0];
-        $text = $token[1];
-
-        if ($id == T_COMMENT || $id == T_DOC_COMMENT) {
-            $text = htmlspecialchars(trim(str_replace('/', '', $text)));
-
-            if ($previousTokenWasComment) {
-                $output .= ' ' . $text;
-            } else {
-                $output .= '' .
-                        '
' . - '
' . - '
' . - $text; - } - - $previousTokenWasComment = true; - } else { - if ($previousTokenWasComment) { - $output .= '
' . - '
' . - '
' .
-                                    '';
-            }
-
-            $output .= htmlspecialchars($text);
-            $previousTokenWasComment = false;
-        }
-    }
-
-    $output .= '' .
-           '
' . - '
' . - '
'; - - return $output; -} - /** * Loads config file into a global * diff --git a/test/BuildTest.php b/test/BuildTest.php index 6ffae18..686999e 100644 --- a/test/BuildTest.php +++ b/test/BuildTest.php @@ -30,7 +30,9 @@ class BuildTest extends BaseTestCase $html = file_get_contents('/tmp/apprentice_output/test.html'); $expectedHtml = "
Test Title
\n" . "
Test Subtitle
\n" . - "
Test Description
\n"; + "
Test Description
\n" . + "
<p>Test comment</p>\n" . + "<pre><code class="language-php">\$test = 'test';</code></pre>
\n"; $this->assertFalse(empty($html)); $this->assertEquals($expectedHtml, $html); @@ -44,7 +46,9 @@ class BuildTest extends BaseTestCase $expectedHtml = "
Test Title
\n" . "
Test Subtitle
\n" . - "
Test Description
\n"; + "
Test Description
\n" . + "
<p>Test comment</p>\n" . + "<pre><code class="language-php">\$test = 'test';</code></pre>
\n"; $expectedHtml2 = "
index
\n"; $html = file_get_contents('/tmp/apprentice_output/test.html'); diff --git a/test/FunctionsTest.php b/test/FunctionsTest.php index de71240..ae8464f 100644 --- a/test/FunctionsTest.php +++ b/test/FunctionsTest.php @@ -50,17 +50,6 @@ class FunctionsTest extends BaseTestCase $this->assertEquals("\n", $icon); } - public function test_code_table() - { - $php = file_get_contents(__DIR__ . '/static/code_table.php'); - $expectedOutput = file_get_contents(__DIR__ . '/static/code_table.html'); - - $html = code_table($php); - - $this->assertFalse(empty($html)); - $this->assertEquals($expectedOutput, $html . "\n"); - } - public function test_partial() { partial('partial', ['test' => 'test var']); diff --git a/test/static/chapter/test.md b/test/static/chapter/test.md new file mode 100644 index 0000000..bbf60f2 --- /dev/null +++ b/test/static/chapter/test.md @@ -0,0 +1,4 @@ +Test comment +```php +$test = 'test'; +``` diff --git a/test/static/code/test.php b/test/static/code/test.php deleted file mode 100644 index f99504c..0000000 --- a/test/static/code/test.php +++ /dev/null @@ -1,4 +0,0 @@ -
<?php
-
-
this is a test
$test = 'some test code';
-
diff --git a/test/static/code_table.php b/test/static/code_table.php deleted file mode 100644 index 6259478..0000000 --- a/test/static/code_table.php +++ /dev/null @@ -1,4 +0,0 @@ - __DIR__ . '/../icons', - 'code_dir' => __DIR__ . '/code', + 'chapter_dir' => __DIR__ . '/chapter', 'templates_dir' => __DIR__ . '/templates', 'output_dir' => '/tmp/apprentice_output', 'files_dir' => __DIR__ . '/files', 'pages' => [ Page::create('index', 'index.phtml'), - Page::create('test', null, 'test.php', [ + Page::create('test', null, 'test.md', [ 'title' => 'Test Title', 'subtitle' => 'Test Subtitle', 'description' => 'Test Description', diff --git a/test/static/templates/default.phtml b/test/static/templates/default.phtml index b76cb44..a08a42d 100644 --- a/test/static/templates/default.phtml +++ b/test/static/templates/default.phtml @@ -1,3 +1,4 @@
+