Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
dc3082d386 | ||
|
6f366b3184 | ||
|
966811c1bf | ||
|
ed216114b5 | ||
|
56464d4ea8 | ||
|
5ed7a72db1 | ||
|
22d42ff657 | ||
|
21d25374b7 | ||
|
4e8b26a3b0 | ||
|
2240682681 | ||
|
ee7c6c4f47 | ||
|
b8af664af3 | ||
|
d6676e76a0 | ||
|
93683a0b33 | ||
|
146a470494 | ||
|
435a4af8ce | ||
|
a32bec51e2 | ||
|
2b6ede2e34 | ||
|
4da3c9eb2a | ||
|
47d2c73835 | ||
|
aa438bb7c0 | ||
|
2ae912aada | ||
|
e09557cf2c |
7
.gitignore
vendored
@@ -1,8 +1,5 @@
|
||||
.DS_Store
|
||||
|
||||
/vendor
|
||||
/public
|
||||
/node_modules
|
||||
|
||||
/docs
|
||||
/.build
|
||||
/.idea
|
||||
/resources
|
||||
|
34
.travis.yml
@@ -1,34 +0,0 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- '7.1'
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
- $HOME/.npm
|
||||
|
||||
before_install:
|
||||
- nvm install 8
|
||||
|
||||
install:
|
||||
- composer update --prefer-dist --no-interaction --prefer-stable --no-suggest
|
||||
- npm update
|
||||
|
||||
script:
|
||||
- npm run prod
|
||||
- php apprentice build
|
||||
- vendor/bin/phpunit
|
||||
|
||||
deploy:
|
||||
provider: pages
|
||||
skip-cleanup: true
|
||||
local-dir: .build
|
||||
target-branch: gh-pages
|
||||
github-token: $GITHUB_TOKEN
|
||||
on:
|
||||
branch: master
|
@@ -1,4 +1,4 @@
|
||||
Copyright 2018 Andrew Davis <andrew@restoredsoftware.com>
|
||||
Copyright 2019 Andrew Davis <andrew@restoredsoftware.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
12
README.md
@@ -4,17 +4,7 @@ It is an online book that currently includes pages for learning the basics of PH
|
||||
PHP Apprentice is a work in progress so any constructive feedback is appreciated.
|
||||
|
||||
## Contributing
|
||||
The codebase is a static site generator. It takes the Markdown files in the `chapters` folder
|
||||
and builds them with PHP templates in `assets/templates`. All of the setup is controlled from the
|
||||
`config.php` file. In `config.php`, the different chapters are ordered and template variables are placed.
|
||||
To make a change to a chapter, just edit the Markdown file and merge with `master` and Travis will automatically
|
||||
build the page again. Any page additions need to be included in the `config.php` file.
|
||||
|
||||
### Running Locally
|
||||
To develop locally, clone the repository and run `composer install` and `npm install` in your terminal.
|
||||
Then, you can run `php apprentice build` to build all the pages and `php apprentice server` to start a
|
||||
development server on `localhost:8080`. The development server automatically rebuilds pages on load,
|
||||
so you can use it to easily view changes. To build the JavaScript and CSS, run `npm run build` or `npm run watch`.
|
||||
PHP Apprentice is built with [Hugo](https://gohugo.io). To build the site locally, clone the repository, install Hugo and run `hugo server` with a terminal in the project root. All of the site pages are written in Markdown and stored in the content directory. To create a new page, the hugo generator will use the default template to generate the markdown file in the content folder: `hugo new test.md`.
|
||||
|
||||
## Roadmap
|
||||
Moving forward, there are three sections I would like to add to the site:
|
||||
|
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
load_config(__DIR__ . '/config.php');
|
||||
|
||||
$app = new Apprentice\App;
|
||||
$app->main();
|
8
archetypes/default.md
Normal file
@@ -0,0 +1,8 @@
|
||||
+++
|
||||
title = "{{ replace .Name "-" " " | title }}"
|
||||
description = ""
|
||||
tags = []
|
||||
draft = true
|
||||
slug = ""
|
||||
previous = ""
|
||||
+++
|
4
assets/dark-mode.js
Normal file
@@ -0,0 +1,4 @@
|
||||
var bodyTag = document.querySelector('body');
|
||||
if (localStorage.getItem('dark_mode') === 'true') {
|
||||
bodyTag.className = 'dark-mode';
|
||||
}
|
@@ -1 +0,0 @@
|
||||
phpapprentice.com
|
@@ -1,3 +0,0 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
Sitemap: https://phpapprentice.com/sitemap.xml
|
@@ -1,117 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset
|
||||
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
|
||||
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
|
||||
<!-- created with Free Online Sitemap Generator www.xml-sitemaps.com -->
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>1.00</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/installing-php.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/basics.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/variables.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/arithmetic.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/strings.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/comparisons.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/boolean-logic.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/conditionals.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/loops.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/arrays.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/functions.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/classes.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/classes-inheritance.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/classes-visibility.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/classes-constructor.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/static.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/interfaces.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/abstract.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/exceptions.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://phpapprentice.com/credits.html</loc>
|
||||
<lastmod>2018-12-27T01:20:29+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
|
||||
|
||||
</urlset>
|
@@ -1,71 +0,0 @@
|
||||
import Prism from 'prismjs';
|
||||
|
||||
var onLoad = function () {
|
||||
Prism.highlightAll();
|
||||
|
||||
var menuButton = document.querySelector('.menu-button');
|
||||
|
||||
// stop execution if menu button does not exist on page
|
||||
if (!menuButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
var modalButton = document.querySelector('.modal-button');
|
||||
var modal = document.querySelector('.modal');
|
||||
var modalContent = document.querySelector('.modal-content');
|
||||
var clickEvent = function (e) {
|
||||
var modal = document.querySelector('.modal');
|
||||
|
||||
if (modal.classList.contains('closed')) {
|
||||
modal.classList.remove('closed')
|
||||
} else {
|
||||
modal.classList.add('closed')
|
||||
}
|
||||
};
|
||||
|
||||
menuButton.addEventListener('click', clickEvent);
|
||||
modalButton.addEventListener('click', clickEvent);
|
||||
modal.addEventListener('click', function (e) {
|
||||
var target = e.target
|
||||
do {
|
||||
if (target == modalContent) {
|
||||
return;
|
||||
}
|
||||
target = target.parentNode;
|
||||
} while (target);
|
||||
|
||||
modal.classList.add('closed')
|
||||
});
|
||||
};
|
||||
|
||||
document.onkeydown = function (e) {
|
||||
e = e || window.event;
|
||||
|
||||
switch (e.key) {
|
||||
case 'Esc':
|
||||
case 'Escape':
|
||||
var modal = document.querySelector('.modal');
|
||||
if (modal && !modal.classList.contains('closed')) {
|
||||
modal.classList.add('closed');
|
||||
}
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
var link = document.querySelector('#next-link');
|
||||
if (link) {
|
||||
window.location.href = link.href;
|
||||
}
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
var link = document.querySelector('#prev-link');
|
||||
if (link) {
|
||||
window.location.href = link.href;
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (document.readyState !== 'loading') {
|
||||
onLoad();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', onLoad);
|
||||
}
|
157
assets/prism-solarized.css
Normal file
@@ -0,0 +1,157 @@
|
||||
/* PrismJS 1.16.0
|
||||
https://prismjs.com/download.html#themes=prism-solarizedlight&languages=markup+css+clike+javascript+markup-templating+http+php */
|
||||
/*
|
||||
Solarized Color Schemes originally by Ethan Schoonover
|
||||
http://ethanschoonover.com/solarized
|
||||
|
||||
Ported for PrismJS by Hector Matos
|
||||
Website: https://krakendev.io
|
||||
Twitter Handle: https://twitter.com/allonsykraken)
|
||||
*/
|
||||
|
||||
/*
|
||||
SOLARIZED HEX
|
||||
--------- -------
|
||||
base03 #002b36
|
||||
base02 #073642
|
||||
base01 #586e75
|
||||
base00 #657b83
|
||||
base0 #839496
|
||||
base1 #93a1a1
|
||||
base2 #eee8d5
|
||||
base3 #fdf6e3
|
||||
yellow #b58900
|
||||
orange #cb4b16
|
||||
red #dc322f
|
||||
magenta #d33682
|
||||
violet #6c71c4
|
||||
blue #268bd2
|
||||
cyan #2aa198
|
||||
green #859900
|
||||
*/
|
||||
|
||||
.light-mode code[class*="language-"],
|
||||
.light-mode pre[class*="language-"] {
|
||||
color: #657b83; /* base00 */
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
.light-mode pre[class*="language-"]::-moz-selection,
|
||||
.light-mode pre[class*="language-"] ::-moz-selection,
|
||||
.light-mode code[class*="language-"]::-moz-selection,
|
||||
.light-mode code[class*="language-"] ::-moz-selection {
|
||||
background: #073642; /* base02 */
|
||||
}
|
||||
|
||||
.light-mode pre[class*="language-"]::selection,
|
||||
.light-mode pre[class*="language-"] ::selection,
|
||||
.light-mode code[class*="language-"]::selection,
|
||||
.light-mode code[class*="language-"] ::selection {
|
||||
background: #073642; /* base02 */
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
.light-mode pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
.light-mode :not(pre) > code[class*="language-"],
|
||||
.light-mode pre[class*="language-"] {
|
||||
background-color: #fdf6e3; /* base3 */
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
.light-mode :not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
}
|
||||
|
||||
.light-mode .token.comment,
|
||||
.light-mode .token.prolog,
|
||||
.light-mode .token.doctype,
|
||||
.light-mode .token.cdata {
|
||||
color: #93a1a1; /* base1 */
|
||||
}
|
||||
|
||||
.light-mode .token.punctuation {
|
||||
color: #586e75; /* base01 */
|
||||
}
|
||||
|
||||
.light-mode .namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.light-mode .token.property,
|
||||
.light-mode .token.tag,
|
||||
.light-mode .token.boolean,
|
||||
.light-mode .token.number,
|
||||
.light-mode .token.constant,
|
||||
.light-mode .token.symbol,
|
||||
.light-mode .token.deleted {
|
||||
color: #268bd2; /* blue */
|
||||
}
|
||||
|
||||
.light-mode .token.selector,
|
||||
.light-mode .token.attr-name,
|
||||
.light-mode .token.string,
|
||||
.light-mode .token.char,
|
||||
.light-mode .token.builtin,
|
||||
.light-mode .token.url,
|
||||
.light-mode .token.inserted {
|
||||
color: #2aa198; /* cyan */
|
||||
}
|
||||
|
||||
.light-mode .token.entity {
|
||||
color: #657b83; /* base00 */
|
||||
background: #eee8d5; /* base2 */
|
||||
}
|
||||
|
||||
.light-mode .token.atrule,
|
||||
.light-mode .token.attr-value,
|
||||
.light-mode .token.keyword {
|
||||
color: #859900; /* green */
|
||||
}
|
||||
|
||||
.light-mode .token.function,
|
||||
.light-mode .token.class-name {
|
||||
color: #b58900; /* yellow */
|
||||
}
|
||||
|
||||
.light-mode .token.regex,
|
||||
.light-mode .token.important,
|
||||
.light-mode .token.variable {
|
||||
color: #cb4b16; /* orange */
|
||||
}
|
||||
|
||||
.light-mode .token.important,
|
||||
.light-mode .token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.light-mode .token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.light-mode .token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
125
assets/prism-tomorrow-night.css
Normal file
@@ -0,0 +1,125 @@
|
||||
/* PrismJS 1.19.0
|
||||
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
|
||||
* Based on https://github.com/chriskempson/tomorrow-theme
|
||||
* @author Rose Pritchard
|
||||
*/
|
||||
|
||||
.dark-mode code[class*="language-"],
|
||||
.dark-mode pre[class*="language-"] {
|
||||
color: #ccc;
|
||||
background: none;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
.dark-mode pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dark-mode :not(pre) > code[class*="language-"],
|
||||
.dark-mode pre[class*="language-"] {
|
||||
background: #2d2d2d;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
.dark-mode :not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.dark-mode .token.comment,
|
||||
.dark-mode .token.block-comment,
|
||||
.dark-mode .token.prolog,
|
||||
.dark-mode .token.doctype,
|
||||
.dark-mode .token.cdata {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.dark-mod .token.punctuation {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.dark-mode .token.tag,
|
||||
.dark-mode .token.attr-name,
|
||||
.dark-mode .token.namespace,
|
||||
.dark-mode .token.deleted {
|
||||
color: #e2777a;
|
||||
}
|
||||
|
||||
.dark-mode .token.function-name {
|
||||
color: #6196cc;
|
||||
}
|
||||
|
||||
.dark-mode .token.boolean,
|
||||
.dark-mode .token.number,
|
||||
.dark-mode .token.function {
|
||||
color: #f08d49;
|
||||
}
|
||||
|
||||
.dark-mode .token.property,
|
||||
.dark-mode .token.class-name,
|
||||
.dark-mode .token.constant,
|
||||
.dark-mode .token.symbol {
|
||||
color: #f8c555;
|
||||
}
|
||||
|
||||
.dark-mode .token.selector,
|
||||
.dark-mode .token.important,
|
||||
.dark-mode .token.atrule,
|
||||
.dark-mode .token.keyword,
|
||||
.dark-mode .token.builtin {
|
||||
color: #cc99cd;
|
||||
}
|
||||
|
||||
.dark-mode .token.string,
|
||||
.dark-mode .token.char,
|
||||
.dark-mode .token.attr-value,
|
||||
.dark-mode .token.regex,
|
||||
.dark-mode .token.variable {
|
||||
color: #7ec699;
|
||||
}
|
||||
|
||||
.dark-mode .token.operator,
|
||||
.dark-mode .token.entity,
|
||||
.dark-mode .token.url {
|
||||
color: #67cdcc;
|
||||
}
|
||||
|
||||
.dark-mode .token.important,
|
||||
.dark-mode .token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.dark-mode .token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.dark-mode .token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.dark-mode .token.inserted {
|
||||
color: green;
|
||||
}
|
||||
|
10
assets/prism.js
Normal file
81
assets/site.js
Normal file
@@ -0,0 +1,81 @@
|
||||
// Dark mode
|
||||
var bodyTag = document.querySelector('body');
|
||||
var darkMode = document.getElementById('dark_mode');
|
||||
if (localStorage.getItem('dark_mode') === 'true') {
|
||||
darkMode.checked = true;
|
||||
} else {
|
||||
darkMode.checked = false;
|
||||
}
|
||||
|
||||
darkMode.addEventListener('change', function (e) {
|
||||
if (this.checked) {
|
||||
localStorage.setItem('dark_mode', true);
|
||||
bodyTag.className = 'dark-mode';
|
||||
} else {
|
||||
localStorage.setItem('dark_mode', false);
|
||||
bodyTag.className = 'light-mode';
|
||||
}
|
||||
});
|
||||
|
||||
// Arrow key bindings
|
||||
document.onkeydown = function (e) {
|
||||
e = e || window.event;
|
||||
|
||||
switch (e.key) {
|
||||
case 'Esc':
|
||||
case 'Escape':
|
||||
var modal = document.querySelector('.modal');
|
||||
if (modal && !modal.classList.contains('closed')) {
|
||||
modal.classList.add('closed');
|
||||
}
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
var link = document.querySelector('#next-link');
|
||||
if (link) {
|
||||
window.location.href = link.href;
|
||||
}
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
var link = document.querySelector('#prev-link');
|
||||
if (link) {
|
||||
window.location.href = link.href;
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Table of contents
|
||||
var menuButton = document.querySelector('.menu-button');
|
||||
if (!menuButton) {
|
||||
throw new Error('No menu button');
|
||||
}
|
||||
|
||||
var modalButton = document.querySelector('.modal-button');
|
||||
var modal = document.querySelector('.modal');
|
||||
var modalContent = document.querySelector('.modal-content');
|
||||
|
||||
var clickEvent = function (e) {
|
||||
var modal = document.querySelector('.modal');
|
||||
|
||||
if (modal.classList.contains('closed')) {
|
||||
modal.classList.remove('closed')
|
||||
} else {
|
||||
modal.classList.add('closed')
|
||||
}
|
||||
};
|
||||
menuButton.addEventListener('click', clickEvent);
|
||||
modalButton.addEventListener('click', clickEvent);
|
||||
|
||||
modal.addEventListener('click', function (e) {
|
||||
var target = e.target
|
||||
do {
|
||||
if (target == modalContent) {
|
||||
return;
|
||||
}
|
||||
target = target.parentNode;
|
||||
} while (target);
|
||||
|
||||
modal.classList.add('closed')
|
||||
});
|
||||
|
||||
|
@@ -1,48 +1,77 @@
|
||||
@import "prismjs/themes/prism-solarizedlight.css";
|
||||
|
||||
$primary-color: #2AA198;
|
||||
$primary-color-dark: #1D6E68;
|
||||
$white: #FFF;
|
||||
$drop-shadow: rgba(0, 0, 0, 0.4);
|
||||
$code-background: #FDF6E3;
|
||||
:root {
|
||||
--primary-color: #2AA198;
|
||||
--primary-color-dark: #1D6E68;
|
||||
--drop-shadow: rgba(0, 0, 0, 0.4);
|
||||
--code-highlight: #EDEDED;
|
||||
--modal-background: #FFF;
|
||||
--button-text: #FFF;
|
||||
--hr-color: #EEE;
|
||||
--dark-background: #222;
|
||||
--dark-text-color: #CCC;
|
||||
--dark-primary-color: #cc99cd; /*#7ec699;*/
|
||||
--dark-primary-color-dark: #8C698C; /*#568769;*/
|
||||
--dark-button-text: #222;
|
||||
--dark-code-highlight: #111;
|
||||
--dark-modal-background: #222;
|
||||
--dark-drop-shadow: rgba(0, 0, 0, 0.6);
|
||||
--dark-hr-color: #444;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif;
|
||||
font-family: Cambria, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "Bitstream Charter", "Caladea", "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif;
|
||||
font-size: 16px;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
body.dark-mode {
|
||||
background-color: var(--dark-background);
|
||||
color: var(--dark-text-color);
|
||||
}
|
||||
button {
|
||||
font-family: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif;
|
||||
font-family: Cambria, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "Bitstream Charter", "Caladea", "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif;
|
||||
}
|
||||
code {
|
||||
background-color: #F0F0F0;
|
||||
font-family: Menlo, Monaco, Consolas, "Noto Mono", "Liberation Mono", "Courier New", monospace !important;
|
||||
font-size: 14px;
|
||||
background-color: var(--code-highlight);
|
||||
font-family: Menlo, Monaco, Consolas, "Ubuntu Mono", "Noto Mono", "Liberation Mono", "Courier New", monospace !important;
|
||||
font-variant-ligatures: none;
|
||||
font-size: 14px !important;
|
||||
padding: 0.10em 0.25em;
|
||||
}
|
||||
.dark-mode code {
|
||||
background-color: var(--dark-code-highlight);
|
||||
}
|
||||
pre, pre code {
|
||||
background-color: transparent;
|
||||
font-family: Menlo, Monaco, Consolas, "Noto Mono", "Liberation Mono", "Courier New", monospace !important;
|
||||
font-size: 14px;
|
||||
font-family: Menlo, Monaco, Consolas, "Ubuntu Mono", "Noto Mono", "Liberation Mono", "Courier New", monospace !important;
|
||||
font-variant-ligatures: none;
|
||||
font-size: 14px !important;
|
||||
margin: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
padding: 0.5em 0 0.5em 0;
|
||||
}
|
||||
a {
|
||||
color: $primary-color; /* #4078f2 */
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
.dark-mode a {
|
||||
color: var(--dark-primary-color);
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
color: $primary-color-dark;
|
||||
color: var(--primary-color-dark);
|
||||
}
|
||||
.dark-mode a:hover {
|
||||
color: var(--dark-primary-color-dark);
|
||||
}
|
||||
hr {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
background-color: #EEE;
|
||||
background-color: var(--hr-color);
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.dark-mode hr {
|
||||
background-color: var(--dark-hr-color);
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
margin-top: 1.5em;
|
||||
@@ -70,10 +99,16 @@ button {
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
color: $primary-color;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.dark-mode button {
|
||||
color: var(--dark-primary-color);
|
||||
}
|
||||
button:hover {
|
||||
color: $primary-color-dark;
|
||||
color: var(--primary-color-dark);
|
||||
}
|
||||
.dark-mode button:hover {
|
||||
color: var(--dark-primary-color-dark);
|
||||
}
|
||||
button:focus {
|
||||
outline: 0;
|
||||
@@ -82,28 +117,45 @@ button .icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
button .icon svg {
|
||||
fill: $primary-color;
|
||||
fill: var(--primary-color);
|
||||
}
|
||||
.dark-mode button .icon svg {
|
||||
fill: var(--dark-primary-color);
|
||||
}
|
||||
button:hover .icon svg {
|
||||
fill: $primary-color-dark;
|
||||
fill: var(--primary-color-dark);
|
||||
}
|
||||
.dark-mode button:hover .icon svg {
|
||||
fill: var(--dark-primary-color-dark);
|
||||
}
|
||||
.button {
|
||||
background-color: $primary-color;
|
||||
color: $white;
|
||||
background-color: var(--primary-color);
|
||||
color: var(--button-text);
|
||||
padding: 0.5em 0.75em;
|
||||
border-radius: 2em;
|
||||
display: inline-block;
|
||||
}
|
||||
.dark-mode .button {
|
||||
color: var(--dark-button-text);
|
||||
background-color: var(--dark-primary-color);
|
||||
}
|
||||
.button:hover {
|
||||
color: $white;
|
||||
background-color: $primary-color-dark;
|
||||
color: var(--button-text);
|
||||
background-color: var(--primary-color-dark);
|
||||
text-decoration: none;
|
||||
}
|
||||
.dark-mode .button:hover {
|
||||
color: var(--dark-button-text);
|
||||
background-color: var(--dark-primary-color-dark);
|
||||
}
|
||||
.button .icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.button .icon svg {
|
||||
fill: $white;
|
||||
fill: var(--button-text);
|
||||
}
|
||||
.dark-mode .button .icon svg {
|
||||
fill: var(--dark-button-text);
|
||||
}
|
||||
.clearfix:after {
|
||||
content: "";
|
||||
@@ -140,19 +192,6 @@ button:hover .icon svg {
|
||||
.body-ol li {
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
.grid-code {
|
||||
display: grid;
|
||||
grid-template-columns: 400px 1fr;
|
||||
grid-column-gap: 3em;
|
||||
}
|
||||
.grid-code .code {
|
||||
background-color: $code-background;
|
||||
}
|
||||
.grid-toc {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
grid-column-gap: 1em;
|
||||
}
|
||||
.navigate-links {
|
||||
margin: 1em 0;
|
||||
float: right;
|
||||
@@ -161,10 +200,18 @@ button:hover .icon svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.navigate-links .icon svg {
|
||||
fill: $primary-color;
|
||||
fill: var(--primary-color);
|
||||
}
|
||||
.navigate-links:hover .icon svg {
|
||||
fill: $primary-color-dark;
|
||||
.dark-mode .navigate-links .icon svg {
|
||||
fill: var(--dark-primary-color);
|
||||
}
|
||||
.prev-link:hover .icon svg,
|
||||
.next-link:hover .icon svg {
|
||||
fill: var(--primary-color-dark);
|
||||
}
|
||||
.dark-mode .prev-link:hover .icon svg,
|
||||
.dark-mode .next-link:hover .icon svg {
|
||||
fill: var(--dark-primary-color-dark);
|
||||
}
|
||||
.navigate-links a {
|
||||
margin-left: 1em;
|
||||
@@ -188,6 +235,10 @@ button:hover .icon svg {
|
||||
height: 1.25em;
|
||||
display: inline-block;
|
||||
}
|
||||
.dark-mode-form {
|
||||
float: right;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.home-title {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -217,7 +268,7 @@ button:hover .icon svg {
|
||||
}
|
||||
@media only screen and (max-width: 420px) {
|
||||
.home-buttons {
|
||||
padding-top: 0.5em;
|
||||
padding-top: 0.5rem;
|
||||
max-width: 15em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -226,13 +277,17 @@ button:hover .icon svg {
|
||||
display: block;
|
||||
}
|
||||
.home-buttons .menu {
|
||||
margin-top: 0.5em;
|
||||
margin-top: 0.5rem;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
.home-buttons .dark-mode-form {
|
||||
float: none;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
.menu {
|
||||
margin-bottom: 1.5em;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.modal {
|
||||
position: fixed;
|
||||
@@ -242,21 +297,28 @@ button:hover .icon svg {
|
||||
left: 0;
|
||||
z-index: 50;
|
||||
overflow: auto;
|
||||
background-color: rgba(0, 0, 0, .4);
|
||||
background-color: var(--drop-shadow);
|
||||
}
|
||||
.dark-mode .modal {
|
||||
background-color: var(--dark-drop-shadow);
|
||||
}
|
||||
.modal-content {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 1em;
|
||||
background-color: $white;
|
||||
background-color: var(--modal-background);
|
||||
max-width: 20em;
|
||||
height: 100%;
|
||||
animation-name: animateleft;
|
||||
animation-duration: .4s;
|
||||
overflow: auto;
|
||||
box-shadow: 0 0 10px 0 $drop-shadow;
|
||||
box-shadow: 0 0 10px 0 var(--drop-shadow);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.dark-mode .modal-content {
|
||||
background-color: var(--dark-modal-background);
|
||||
box-shadow: 0 0 10px 0 var(--dark-drop-shadow);
|
||||
}
|
||||
.modal-content .table-of-contents {
|
||||
padding: 2em;
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
<?php partial('header', ['title' => 'PHP Apprentice']) ?>
|
||||
|
||||
<div class="container center">
|
||||
<a href="/" class="logo-404"><?= icon('elephant') ?></a>
|
||||
<p class="message-404">Whoops! The page could not be found.</p>
|
||||
<div class="toc-404">
|
||||
<?php partial('table_of_contents') ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php partial('footer') ?>
|
@@ -1,3 +0,0 @@
|
||||
</body>
|
||||
|
||||
</html>
|
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title><?= $title ?? 'PHP Apprentice' ?></title>
|
||||
<meta name="description" content="<?= $subtitle ?? 'An online book for learning PHP' ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="<?php echo asset('css/site.css') ?>">
|
||||
<link rel="icon" href="/favicon-32.png">
|
||||
<script src="<?php echo asset('js/site.js') ?>"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
@@ -1,6 +0,0 @@
|
||||
<div class="menu">
|
||||
<button class="menu-button" title="Open Menu">
|
||||
<div class="icon"><?= icon('show-sidebar') ?></div>
|
||||
Table of Contents
|
||||
</button>
|
||||
</div>
|
@@ -1,38 +0,0 @@
|
||||
<div class="table-of-contents">
|
||||
<h4>Table of Contents</h4>
|
||||
<ul class="list-plain">
|
||||
<li><a href="/">Preface</a></li>
|
||||
<li><a href="<?= page_path('installing-php') ?>">Installing PHP</a></li>
|
||||
</ul>
|
||||
|
||||
<h5 class="section-title">Basics</h5>
|
||||
<ol>
|
||||
<li><a href="<?= page_path('basics') ?>">Basics</a></li>
|
||||
<li><a href="<?= page_path('variables') ?>">Variables</a></li>
|
||||
<li><a href="<?= page_path('arithmetic') ?>">Arithmetic</a></li>
|
||||
<li><a href="<?= page_path('strings') ?>">Strings</a></li>
|
||||
<li><a href="<?= page_path('comparisons') ?>">Comparisons</a></li>
|
||||
<li><a href="<?= page_path('boolean-logic') ?>">Boolean Logic</a></li>
|
||||
<li><a href="<?= page_path('conditionals') ?>">Conditionals</a></li>
|
||||
<li><a href="<?= page_path('loops') ?>">Loops</a></li>
|
||||
<li><a href="<?= page_path('arrays') ?>">Arrays</a></li>
|
||||
<li><a href="<?= page_path('functions') ?>">Functions</a></li>
|
||||
<li><a href="<?= page_path('classes') ?>">Classes</a></li>
|
||||
<li><a href="<?= page_path('classes-inheritance') ?>">Classes: Inheritance</a></li>
|
||||
<li><a href="<?= page_path('classes-visibility') ?>">Classes: Visibility</a></li>
|
||||
<li><a href="<?= page_path('classes-constructor') ?>">Classes: Constructor</a></li>
|
||||
<li><a href="<?= page_path('static') ?>">Static</a></li>
|
||||
<li><a href="<?= page_path('interfaces') ?>">Interfaces</a></li>
|
||||
<li><a href="<?= page_path('abstract') ?>">Abstract Classes</a></li>
|
||||
<li><a href="<?= page_path('exceptions') ?>">Exceptions</a></li>
|
||||
</ol>
|
||||
|
||||
<h5 class="section-title">Web</h5>
|
||||
<ol>
|
||||
<li><a href="<?= page_path('web/http') ?>">HTTP</a></li>
|
||||
<li><a href="<?= page_path('web/http-post') ?>">HTTP POST</a></li>
|
||||
<li><a href="<?= page_path('web/http-server') ?>">PHP HTTP Server</a></li>
|
||||
</ol>
|
||||
|
||||
<a href="<?= page_path('credits') ?>">Credits</a>
|
||||
</div>
|
@@ -1,41 +0,0 @@
|
||||
<?php partial('header', ['title' => 'PHP Apprentice - Credits']) ?>
|
||||
|
||||
<div class="container small center">
|
||||
<?php partial('menu_button') ?>
|
||||
|
||||
<div>
|
||||
<h1>Credits</h1>
|
||||
<p>
|
||||
PHP Apprentice was inspired by
|
||||
<a href="https://gobyexample.com/">Go By Example</a> and by <a href="https://elixirschool.com/">Elixir School</a>. 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.
|
||||
</p>
|
||||
<p>
|
||||
Sites used as a reference while writing PHP Apprentice:
|
||||
<ul>
|
||||
<li><a href="https://secure.php.net/" target="_blank">php.net</a></li>
|
||||
<li><a href="https://www.phptherightway.com/" target="_blank">PHP The Right Way</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
PHP Apprentice was built using several open source projects, besides PHP itself.
|
||||
Thank you to each of these maintainers for providing great libraries!
|
||||
<ul>
|
||||
<li><a href="https://prismjs.com/" target="_blank">Prism.js</a></li>
|
||||
<li><a href="https://www.zondicons.com/" target="_blank">Zondicons by Steve Schoger</a></li>
|
||||
<li><a href="https://postcss.org/" target="_blank">PostCSS</a></li>
|
||||
<li><a href="https://symfony.com/doc/current/components/console.html" target="_blank">Symfony CLI</a></li>
|
||||
<li><a href="https://symfony.com/doc/current/frontend.html" target="_blank">Symfony Encore</a></li>
|
||||
<li><a href="https://parsedown.org/" target="_blank">Parsedown</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
<hr />
|
||||
<p>
|
||||
Created and managed by <a href="https://twitter.com/restoreddev" target="_blank">Andrew Davis @restoreddev</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<?php partial('menu_modal') ?>
|
||||
<?php partial('footer') ?>
|
@@ -1,29 +0,0 @@
|
||||
<?php partial('header', ['title' => $title, 'subtitle' => $subtitle]) ?>
|
||||
|
||||
<div class="container small center">
|
||||
<?php partial('menu_button') ?>
|
||||
|
||||
<h1><?= escape($title) ?></h1>
|
||||
<h3 class="subtitle"><?= escape($subtitle) ?></h3>
|
||||
<?= $chapter ?>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div class="navigate-links">
|
||||
<?php if (!empty($previous)): ?>
|
||||
<a href="<?= page_path($previous) ?>" title="Previous" id="prev-link">
|
||||
<div class="icon"><?= icon('cheveron-outline-left') ?></div>
|
||||
Previous
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<?php if (!empty($next)): ?>
|
||||
<a href="<?= page_path($next) ?>" title="Next" id="next-link">
|
||||
Next
|
||||
<div class="icon"><?= icon('cheveron-outline-right') ?></div>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<?php partial('menu_modal') ?>
|
||||
<?php partial('footer') ?>
|
@@ -1,35 +0,0 @@
|
||||
<?php partial('header', ['title' => 'PHP Apprentice']) ?>
|
||||
|
||||
<div class="container small center">
|
||||
<div>
|
||||
<div class="home-title-wrapper">
|
||||
<div class="home-logo"><?= icon('elephant') ?></div>
|
||||
<h1 class="home-title">PHP Apprentice</h1>
|
||||
<h3 class="home-subtitle">An online book for learning PHP</h3>
|
||||
</div>
|
||||
<p>
|
||||
PHP Apprentice is an online, open source book about the PHP programming language. PHP is one of the most popular platforms for building websites and web services. It is a great language that is easy to learn and allows you to build powerful and complex web applications very quickly.
|
||||
</p>
|
||||
<p>
|
||||
The goal of PHP Apprentice is to be an easy to understand resource for learning how to write good code in PHP. There are a lot of PHP tutorials on the internet that use outdated practices or insecure code. I want this book to show how to write PHP code with quality.
|
||||
</p>
|
||||
<p>
|
||||
The contents of PHP Apprentice are for beginners and experienced PHP developers. The book currently has content for learning the basics of the language. In the future, more pages will be added for more advanced topics like building websites, database integration and security.
|
||||
</p>
|
||||
<p>
|
||||
PHP Apprentice is currently a work in progress. If you would like to give feedback or request a certain discussion topic, check out the <a href="https://github.com/restoreddev/phpapprentice" target="_blank">GitHub repository</a>.
|
||||
</p>
|
||||
<div class="home-buttons">
|
||||
<a href="<?= page_path('basics') ?>" class="button">
|
||||
<div class="icon"><?= icon('book-reference') ?></div>
|
||||
Open First Chapter
|
||||
</a>
|
||||
<?php partial('menu_button') ?>
|
||||
</div>
|
||||
<hr />
|
||||
<p>Created and managed by <a href="https://twitter.com/restoreddev" target="_blank">Andrew Davis @restoreddev</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php partial('menu_modal') ?>
|
||||
<?php partial('footer') ?>
|
@@ -1,29 +0,0 @@
|
||||
<?php partial('header', ['title' => 'PHP Apprentice - Installing PHP']) ?>
|
||||
|
||||
<div class="container small center">
|
||||
<?php partial('menu_button') ?>
|
||||
|
||||
<div>
|
||||
<h1>Installing PHP</h1>
|
||||
<h2>Windows 10</h2>
|
||||
<ol class="body-ol">
|
||||
<li>Download PHP from the <a href="https://windows.php.net/download">PHP For Windows</a> 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.</li>
|
||||
<li>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.</li>
|
||||
<li>Install the VC redistrubutable using the downloaded executable.</li>
|
||||
<li>The PHP download comes in a zip folder. Extract the zip folder into your user directory in a folder named <code>php</code>. The path should look like <code>C:\Users\username\php</code>.</li>
|
||||
<li>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.</li>
|
||||
<li>Now, you can open PowerShell or Command Prompt and type <code>php -v</code> to verify PHP was installed correctly. It will return the installed version of PHP on your system.</li>
|
||||
</ol>
|
||||
|
||||
<h2>MacOS</h2>
|
||||
<p>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 <code>php -v</code>. You will need at least PHP 7.1 on your computer to use all the features in these tutorials.</p>
|
||||
<ol class="body-ol">
|
||||
<li>Install <a href="https://brew.sh/">Homebrew</a>.</li>
|
||||
<li>Run <code>brew install php</code> in Terminal.</li>
|
||||
<li>Check your version is correct by running <code>php -v</code> in Terminal.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php partial('menu_modal') ?>
|
||||
<?php partial('footer') ?>
|
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "restoreddev/phpapprentice",
|
||||
"description": "A site for learning PHP",
|
||||
"require": {
|
||||
"php": "^7.1.3",
|
||||
"symfony/console": "^4.1",
|
||||
"erusev/parsedown": "^1.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Apprentice\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/util/functions.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Test\\": "test/"
|
||||
}
|
||||
}
|
||||
}
|
1599
composer.lock
generated
177
config.php
@@ -1,177 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Apprentice\Page;
|
||||
|
||||
return [
|
||||
/*
|
||||
*
|
||||
* Directory for SVG icons that can be used in templates
|
||||
*
|
||||
*/
|
||||
'icon_dir' => __DIR__ . '/assets/icons',
|
||||
|
||||
/*
|
||||
*
|
||||
* Directory holding chapter files
|
||||
*
|
||||
*/
|
||||
'chapter_dir' => __DIR__ . '/chapters',
|
||||
|
||||
/*
|
||||
*
|
||||
* Directory to PHP templates used by pages
|
||||
*
|
||||
*/
|
||||
'templates_dir' => __DIR__ . '/assets/templates',
|
||||
|
||||
/*
|
||||
*
|
||||
* Output directory for html files and assets
|
||||
*
|
||||
*/
|
||||
'output_dir' => __DIR__ . '/.build',
|
||||
|
||||
/*
|
||||
*
|
||||
* Static files that should be loaded into output directory
|
||||
*
|
||||
*/
|
||||
'files_dir' => __DIR__ . '/assets/files',
|
||||
|
||||
/*
|
||||
*
|
||||
* Configuration for all pages on the site
|
||||
*
|
||||
*/
|
||||
'pages' => [
|
||||
Page::create('index', null, [], 'index.phtml'),
|
||||
Page::create('installing-php', null, [], 'installing-php.phtml'),
|
||||
Page::create('credits', null, [], 'credits.phtml'),
|
||||
Page::create('404', null, [], '404.phtml'),
|
||||
Page::create('basics', 'basics/01-basics.md', [
|
||||
'title' => 'Basics',
|
||||
'subtitle' => 'Getting started',
|
||||
'next' => 'variables',
|
||||
]),
|
||||
Page::create('variables', 'basics/02-variables.md', [
|
||||
'title' => 'Variables',
|
||||
'subtitle' => 'The building blocks of PHP',
|
||||
'previous' => 'basics',
|
||||
'next' => 'arithmetic',
|
||||
]),
|
||||
Page::create('arithmetic', 'basics/03-arithmetic.md', [
|
||||
'title' => 'Arithmetic',
|
||||
'subtitle' => 'Doing math like a pro',
|
||||
'previous' => 'variables',
|
||||
'next' => 'strings',
|
||||
]),
|
||||
Page::create('strings', 'basics/04-strings.md', [
|
||||
'title' => 'Strings',
|
||||
'subtitle' => 'Working with text',
|
||||
'previous' => 'arithmetic',
|
||||
'next' => 'comparisons',
|
||||
]),
|
||||
Page::create('comparisons', 'basics/05-comparisons.md', [
|
||||
'title' => 'Comparisons',
|
||||
'subtitle' => 'Equality checking',
|
||||
'previous' => 'strings',
|
||||
'next' => 'boolean-logic',
|
||||
]),
|
||||
Page::create('boolean-logic', 'basics/06-boolean-logic.md', [
|
||||
'title' => 'Boolean Logic',
|
||||
'subtitle' => 'Is it a yes or a no?',
|
||||
'previous' => 'comparisons',
|
||||
'next' => 'conditionals',
|
||||
]),
|
||||
Page::create('conditionals', 'basics/07-conditionals.md', [
|
||||
'title' => 'Conditionals',
|
||||
'subtitle' => 'Checking the if before the what',
|
||||
'previous' => 'boolean-logic',
|
||||
'next' => 'loops',
|
||||
]),
|
||||
Page::create('loops', 'basics/08-loops.md', [
|
||||
'title' => 'Loops',
|
||||
'subtitle' => 'Increase your repetitions',
|
||||
'previous' => 'conditionals',
|
||||
'next' => 'arrays',
|
||||
]),
|
||||
Page::create('arrays', 'basics/09-arrays.md', [
|
||||
'title' => 'Arrays',
|
||||
'subtitle' => 'Time to make a list',
|
||||
'previous' => 'loops',
|
||||
'next' => 'functions',
|
||||
]),
|
||||
Page::create('functions', 'basics/10-functions.md', [
|
||||
'title' => 'Functions',
|
||||
'subtitle' => 'Reusable code',
|
||||
'previous' => 'arrays',
|
||||
'next' => 'classes',
|
||||
]),
|
||||
Page::create('classes', 'basics/11-classes.md', [
|
||||
'title' => 'Classes',
|
||||
'subtitle' => 'Object-oriented programming',
|
||||
'previous' => 'functions',
|
||||
'next' => 'classes-inheritance',
|
||||
]),
|
||||
Page::create('classes-inheritance', 'basics/12-classes-inheritance.md', [
|
||||
'title' => 'Classes: Inheritance',
|
||||
'subtitle' => 'Extend your objects',
|
||||
'previous' => 'classes',
|
||||
'next' => 'classes-visibility',
|
||||
]),
|
||||
Page::create('classes-visibility', 'basics/13-classes-visibility.md', [
|
||||
'title' => 'Classes: Visibility',
|
||||
'subtitle' => 'Privatizing your objects',
|
||||
'previous' => 'classes-inheritance',
|
||||
'next' => 'classes-constructor',
|
||||
]),
|
||||
Page::create('classes-constructor', 'basics/14-classes-constructor.md', [
|
||||
'title' => 'Classes: Constructor',
|
||||
'subtitle' => 'Construct your objects',
|
||||
'previous' => 'classes-visibility',
|
||||
'next' => 'static',
|
||||
]),
|
||||
Page::create('static', 'basics/15-static.md', [
|
||||
'title' => 'Static',
|
||||
'subtitle' => 'Class properties and methods',
|
||||
'previous' => 'classes-constructor',
|
||||
'next' => 'interfaces',
|
||||
]),
|
||||
Page::create('interfaces', 'basics/16-interfaces.md', [
|
||||
'title' => 'Interfaces',
|
||||
'subtitle' => 'Writing code contracts',
|
||||
'previous' => 'static',
|
||||
'next' => 'abstract',
|
||||
]),
|
||||
Page::create('abstract', 'basics/17-abstract.md', [
|
||||
'title' => 'Abstract Classes',
|
||||
'subtitle' => 'Inheriting an interface',
|
||||
'previous' => 'interfaces',
|
||||
'next' => 'exceptions',
|
||||
]),
|
||||
Page::create('exceptions', 'basics/18-exceptions.md', [
|
||||
'title' => 'Exceptions',
|
||||
'subtitle' => 'Throwing errors',
|
||||
'previous' => 'abstract',
|
||||
'next' => 'web/http',
|
||||
]),
|
||||
Page::create('web/http', 'web/01-http.md', [
|
||||
'title' => 'HTTP',
|
||||
'subtitle' => 'Understanding the format of the web',
|
||||
'previous' => 'exceptions',
|
||||
'next' => 'web/http-post',
|
||||
]),
|
||||
Page::create('web/http-post', 'web/02-http-post.md', [
|
||||
'title' => 'HTTP POST',
|
||||
'subtitle' => 'Sending data to a server',
|
||||
'previous' => 'web/http',
|
||||
'next' => 'web/http-server',
|
||||
]),
|
||||
Page::create('web/http-server', 'web/03-http-server.md', [
|
||||
'title' => 'PHP HTTP Server',
|
||||
'subtitle' => 'Handling HTTP Requests in PHP',
|
||||
'previous' => 'web/http-post',
|
||||
'next' => '',
|
||||
]),
|
||||
],
|
||||
];
|
8
config.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
baseURL = "https://phpapprentice.com/"
|
||||
languageCode = "en-us"
|
||||
title = "PHP Apprentice"
|
||||
uglyurls = true
|
||||
|
||||
[markup]
|
||||
[markup.highlight]
|
||||
codeFences = false
|
@@ -1,3 +1,10 @@
|
||||
+++
|
||||
title = "Basics"
|
||||
description = "Getting Started"
|
||||
tags = ["php", "basics"]
|
||||
slug = "basics"
|
||||
next = "variables.html"
|
||||
+++
|
||||
If you want to follow along by writing code, start by downloading a code editor. I recommend
|
||||
[Visual Studio Code](https://code.visualstudio.com/) or [Sublime Text](https://www.sublimetext.com/).
|
||||
Next, create a new file in your editor called `basics.php` and save it anywhere on your computer, like a folder
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Variables"
|
||||
description = "The building blocks of PHP"
|
||||
tags = ["php", "variables"]
|
||||
slug = "variables"
|
||||
previous = "basics.html"
|
||||
next = "arithmetic.html"
|
||||
+++
|
||||
The variable is the basic building block of any programming language.
|
||||
In PHP, all variables start with a dollar sign.
|
||||
```php
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Arithmetic"
|
||||
description = "Doing math like a pro"
|
||||
tags = ["php", "arithmetic", "math"]
|
||||
slug = "arithmetic"
|
||||
previous = "variables.html"
|
||||
next = "strings.html"
|
||||
+++
|
||||
Now that we know how to create variables, let's look at doing some math.
|
||||
```php
|
||||
<?php
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Strings"
|
||||
description = "Working with text"
|
||||
tags = ["php", "strings"]
|
||||
slug = "strings"
|
||||
previous = "arithmetic.html"
|
||||
next = "comparisons.html"
|
||||
+++
|
||||
As seen in the first chapter, a string is a group of characters created by
|
||||
surrounding text in single or double quotes.
|
||||
```php
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Comparisons"
|
||||
description = "Equality checking"
|
||||
tags = ["php", "comparisons"]
|
||||
slug = "comparisons"
|
||||
previous = "strings.html"
|
||||
next = "boolean-logic.html"
|
||||
+++
|
||||
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.
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Boolean Logic"
|
||||
description = "Is it a yes or a no?"
|
||||
tags = ["php", "booleans"]
|
||||
slug = "boolean-logic"
|
||||
previous = "comparisons.html"
|
||||
next = "conditionals.html"
|
||||
+++
|
||||
Boolean logic is used to combine booleans to return another boolean.
|
||||
|
||||
Using double ampersands tells PHP to check if both values are true.
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Conditionals"
|
||||
description = "Checking the if before the what"
|
||||
tags = ["php", "conditionals", "if", "switch"]
|
||||
slug = "conditionals"
|
||||
previous = "boolean-logic.html"
|
||||
next = "loops.html"
|
||||
+++
|
||||
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.
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Loops"
|
||||
description = "Increase your repetitions"
|
||||
tags = ["php", "loop", "foreach", "for"]
|
||||
slug = "loops"
|
||||
previous = "conditionals.html"
|
||||
next = "arrays.html"
|
||||
+++
|
||||
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.
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Arrays"
|
||||
description = "Time to make a list"
|
||||
tags = ["php", "array", "list"]
|
||||
slug = "arrays"
|
||||
previous = "loops.html"
|
||||
next = "functions.html"
|
||||
+++
|
||||
In PHP, arrays are used to store a list of items in a single variable.
|
||||
There are two ways to create an array.
|
||||
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Functions"
|
||||
description = "Reusable code"
|
||||
tags = ["php", "function"]
|
||||
slug = "functions"
|
||||
previous = "arrays.html"
|
||||
next = "classes.html"
|
||||
+++
|
||||
A function allows you to store code under a name and then execute
|
||||
that code later.
|
||||
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Classes"
|
||||
description = "Object-oriented programming"
|
||||
tags = ["php", "class", "object-oriented programming"]
|
||||
slug = "classes"
|
||||
previous = "functions.html"
|
||||
next = "classes-inheritance.html"
|
||||
+++
|
||||
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
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Classes: Inheritance"
|
||||
description = "Extend your objects"
|
||||
tags = ["php", "extend", "inheritance"]
|
||||
slug = "classes-inheritance"
|
||||
previous = "classes.html"
|
||||
next = "classes-visibility.html"
|
||||
+++
|
||||
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.
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Classes: Visibility"
|
||||
description = "Privatizing your objects"
|
||||
tags = ["php", "private", "protected", "visibility"]
|
||||
slug = "classes-visibility"
|
||||
previous = "classes-inheritance.html"
|
||||
next = "classes-constructor.html"
|
||||
+++
|
||||
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.
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Classes: Constructor"
|
||||
description = "Construct your objects"
|
||||
tags = ["php", "construct", "constructor", "new"]
|
||||
slug = "classes-constructor"
|
||||
previous = "classes-visibility.html"
|
||||
next = "static.html"
|
||||
+++
|
||||
Whenever you create an object in PHP, you put parentheses after the class name.
|
||||
In the previous examples, we always left the parentheses empty.
|
||||
```php
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Static"
|
||||
description = "Class properties and methods"
|
||||
tags = ["php", "static"]
|
||||
slug = "static"
|
||||
previous = "classes-constructor.html"
|
||||
next = "interfaces.html"
|
||||
+++
|
||||
When writing a class, all of the properties and methods are being defined for the object
|
||||
that will be created from the class.
|
||||
```php
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Interfaces"
|
||||
description = "Writing code contracts"
|
||||
tags = ["php", "interface"]
|
||||
slug = "interfaces"
|
||||
previous = "static.html"
|
||||
next = "abstract.html"
|
||||
+++
|
||||
The word `interface` is a confusing term because it is used for so many different concepts.
|
||||
Most often, we use it to describe the appearance of an app and how a user interacts with it.
|
||||
However, in PHP, an interface is a special construct that acts as a contract for classes.
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Abstract Classes"
|
||||
description = "Inheriting an interface"
|
||||
tags = ["php", "abstract"]
|
||||
slug = "abstract"
|
||||
previous = "interfaces.html"
|
||||
next = "exceptions.html"
|
||||
+++
|
||||
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.
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "Exceptions"
|
||||
description = "Throwing errors"
|
||||
tags = ["php", "exception"]
|
||||
slug = "exceptions"
|
||||
previous = "abstract.html"
|
||||
next = "web/http.html"
|
||||
+++
|
||||
Sometimes things go wrong when someone uses your code. How do we handle this situation?
|
||||
PHP has Exceptions to define errors and the ability to `throw` them to stop code
|
||||
execution and tell the user of your code that something is wrong.
|
35
content/credits.html
Normal file
@@ -0,0 +1,35 @@
|
||||
+++
|
||||
title = "Credits"
|
||||
+++
|
||||
<div>
|
||||
<p>
|
||||
PHP Apprentice was inspired by
|
||||
<a href="https://gobyexample.com/">Go By Example</a> and by <a href="https://elixirschool.com/">Elixir School</a>. 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.
|
||||
</p>
|
||||
<p>
|
||||
Sites used as a reference while writing PHP Apprentice:
|
||||
<ul>
|
||||
<li><a href="https://secure.php.net/" target="_blank">php.net</a></li>
|
||||
<li><a href="https://www.phptherightway.com/" target="_blank">PHP The Right Way</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
PHP Apprentice was built using several open source projects, besides PHP itself.
|
||||
Thank you to each of these maintainers for providing great libraries!
|
||||
<ul>
|
||||
<li><a href="https://prismjs.com/" target="_blank">Prism.js</a></li>
|
||||
<li><a href="https://www.zondicons.com/" target="_blank">Zondicons by Steve Schoger</a></li>
|
||||
<li><a href="https://postcss.org/" target="_blank">PostCSS</a></li>
|
||||
<li><a href="https://symfony.com/doc/current/components/console.html" target="_blank">Symfony CLI</a></li>
|
||||
<li><a href="https://symfony.com/doc/current/frontend.html" target="_blank">Symfony Encore</a></li>
|
||||
<li><a href="https://parsedown.org/" target="_blank">Parsedown</a></li>
|
||||
<li><a href="https://github.com/altercation/solarized" target="_blank">Solarized Theme</a></li>
|
||||
<li><a href="https://github.com/chriskempson/tomorrow-theme" target="_blank">Tomorrow Theme</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
<hr />
|
||||
<p>
|
||||
Created and managed by <a href="https://twitter.com/restoreddev" target="_blank">Andrew Davis @restoreddev</a>.
|
||||
</p>
|
||||
</div>
|
22
content/installing-php.html
Normal file
@@ -0,0 +1,22 @@
|
||||
+++
|
||||
title = "Installing PHP"
|
||||
+++
|
||||
<div>
|
||||
<h2>Windows 10</h2>
|
||||
<ol class="body-ol">
|
||||
<li>Download PHP from the <a href="https://windows.php.net/download">PHP For Windows</a> 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.</li>
|
||||
<li>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.</li>
|
||||
<li>Install the VC redistrubutable using the downloaded executable.</li>
|
||||
<li>The PHP download comes in a zip folder. Extract the zip folder into your user directory in a folder named <code>php</code>. The path should look like <code>C:\Users\username\php</code>.</li>
|
||||
<li>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.</li>
|
||||
<li>Now, you can open PowerShell or Command Prompt and type <code>php -v</code> to verify PHP was installed correctly. It will return the installed version of PHP on your system.</li>
|
||||
</ol>
|
||||
|
||||
<h2>MacOS</h2>
|
||||
<p>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 <code>php -v</code>. You will need at least PHP 7.1 on your computer to use all the features in these tutorials.</p>
|
||||
<ol class="body-ol">
|
||||
<li>Install <a href="https://brew.sh/">Homebrew</a>.</li>
|
||||
<li>Run <code>brew install php</code> in Terminal.</li>
|
||||
<li>Check your version is correct by running <code>php -v</code> in Terminal.</li>
|
||||
</ol>
|
||||
</div>
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "HTTP"
|
||||
description = "Understanding the format of the web"
|
||||
tags = ["web", "http"]
|
||||
slug = "http"
|
||||
previous = "exceptions.html"
|
||||
next = "web/http-post.html"
|
||||
+++
|
||||
HTTP stands for Hypertext Transfer Protocol. It is a standard for how requests and responses should be formatted for
|
||||
a server and a web browser. When you open a link in your web browser, you are sending a HTTP request to a server and it
|
||||
is responding with a HTTP response. The browser then takes the response, parses it and displays it to the user.
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "HTTP Post"
|
||||
description = "Sending data to a server"
|
||||
tags = ["web", "http", "post"]
|
||||
slug = "http-post"
|
||||
previous = "web/http.html"
|
||||
next = "web/http-server.html"
|
||||
+++
|
||||
HTTP uses multiple different request types for indicating actions that should be performed on the server. The most common ones
|
||||
you will use are:
|
||||
- GET -> Retrieve a resource
|
@@ -1,3 +1,11 @@
|
||||
+++
|
||||
title = "PHP HTTP Server"
|
||||
description = "Handling HTTP Requests in PHP"
|
||||
tags = ["php", "http", "server"]
|
||||
slug = "http-server"
|
||||
previous = "web/http-post.html"
|
||||
next ="web/php-html.html"
|
||||
+++
|
||||
In PHP, you usually use a separate web server program that accepts HTTP requests and passes them to PHP to create a response. Common examples of separate web server programs are Apache and Nginx. However, PHP has a built in web server we can use during development.
|
||||
|
||||
To use the development web server, open a terminal and a new folder for holding your code. Create a file called `index.php` and put this PHP code in it:
|
46
content/web/04-php-html.md
Normal file
@@ -0,0 +1,46 @@
|
||||
+++
|
||||
title = "PHP HTML"
|
||||
description = "Serving HTML using PHP"
|
||||
tags = ["php", "http", "server"]
|
||||
slug = "php-html"
|
||||
previous = "web/http-server.html"
|
||||
+++
|
||||
The most common content type of HTTP messages is HTML. Hypertext Markup Language is the anchor of all websites. It is the language that defines how content should be displayed in a web page. Just as we saw in the last chapter with HTTP messages, PHP makes it easy to output HTML. In fact, PHP was built around enhancing HTML.
|
||||
|
||||
The Mozilla Developer Network has an [excellent tutorial](https://developer.mozilla.org/en-US/docs/Web/HTML) on HTML. I will assume that you can already write some HTML, however, I will use basic tags for this tutorial.
|
||||
|
||||
We can start by creating a new `index.php` file and running the localhost server: `php -S localhost:8080`. However, we are not going to start the file with a PHP tag. We are going to write some HTML.
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>PHP Apprentice - HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>I can write HTML!</h1>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
If you open `http://localhost:8080` in your web browser, you will see `I can write HTML!` in big letters. PHP automatically sees that you have written some HTML and responds to the browser's GET request with the appropriate HTTP response headers and the body set to the HTML text.
|
||||
|
||||
How does the PHP runtime know when you are writing HTML or writing PHP code? It all depends on PHP tags. When you write `<?php`, the PHP runtime will interpret anything after that tag as PHP code. This is the reason why PHP files with only PHP code must start with `<?php`. When you want to stop writing PHP code, you can use `?>` as the closing tag. In PHP only files, the closing tag is unnecessary since the entire file is PHP.
|
||||
|
||||
Using the PHP open and close tags, we can embed PHP code inside an HTML tag. For example, we can make our HTML message all caps using PHP.
|
||||
```php
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>PHP Apprentice - HTML</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1><?php echo strtoupper('I can write HTML!'); ?></h1>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
Refresh your browser and you will see all the text in uppercase letters. When I wanted to run some PHP code, I used the PHP open tag: `<?php`. When I wanted to return to writing HTML, I used the closing tag: `?>`. In this way, you can easily output HTML documents to web browsers, while running code. Embedding PHP gives you the flexibility to create dynamic web pages.
|
||||
|
||||
I used the PHP keyword `echo` in the last example to output the string result of `strtoupper` inside the `<h1>` tags. PHP supports a shorthand opening tag that combines the open tag with echo: `<?=`. You could write the above example like the following.
|
||||
```php
|
||||
<h1><?= strtoupper('I can write HTML!'); ?></h1>
|
||||
```
|
||||
This example and the previous one are equivalent to the PHP runtime.
|
9
layouts/404.html
Normal file
@@ -0,0 +1,9 @@
|
||||
{{ define "main" }}
|
||||
<div class="container center">
|
||||
<a href="/" class="logo-404">{{ readFile "static/elephant.svg" | safeHTML }}</a>
|
||||
<p class="message-404">Whoops! The page could not be found.</p>
|
||||
<div class="toc-404">
|
||||
{{ partial "table_of_contents.html" . }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
39
layouts/_default/baseof.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>{{ if .Title }}{{ .Title }}{{ else }}{{ .Site.Title }}{{ end }}</title>
|
||||
<meta name="description" content="{{ if .Description }}{{ .Description }}{{ else }}An online book for learning PHP{{ end }}" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
{{ $prismSolarized := resources.Get "prism-solarized.css" }}
|
||||
{{ $prismTomorrowNight := resources.Get "prism-tomorrow-night.css" }}
|
||||
{{ $securePrismSolarized := $prismSolarized | resources.Fingerprint "sha512" }}
|
||||
{{ $securePrismTomorrowNight := $prismTomorrowNight | resources.Fingerprint "sha512" }}
|
||||
|
||||
{{ $css := resources.Get "styles.css" }}
|
||||
{{ $style := $css | resources.PostCSS }}
|
||||
{{ $secureCss := $style | resources.Fingerprint "sha512" }}
|
||||
<link rel="stylesheet" type="text/css" href="{{ $securePrismSolarized.Permalink }}" integrity="{{ $securePrismSolarized.Data.Integrity }}">
|
||||
<link rel="stylesheet" type="text/css" href="{{ $securePrismTomorrowNight.Permalink }}" integrity="{{ $securePrismTomorrowNight.Data.Integrity }}">
|
||||
<link rel="stylesheet" type="text/css" href="{{ $secureCss.Permalink }}" integrity="{{ $secureCss.Data.Integrity }}">
|
||||
<link rel="icon" href="/favicon-32.png">
|
||||
</head>
|
||||
<body class="light-mode">
|
||||
{{ $darkMode := resources.Get "dark-mode.js" }}
|
||||
{{ $darkModeSecure := $darkMode | resources.Fingerprint "sha512" }}
|
||||
<script type="text/javascript" src="{{ $darkModeSecure.Permalink }}" integrity="{{ $darkModeSecure.Data.Integrity }}"></script>
|
||||
|
||||
{{ block "main" . }}{{ end }}
|
||||
|
||||
{{ $prism := resources.Get "prism.js" }}
|
||||
{{ $site := resources.Get "site.js"}}
|
||||
{{ $prismSecure := $prism | resources.Fingerprint "sha512" }}
|
||||
{{ $siteSecure := $site | resources.Fingerprint "sha512" }}
|
||||
<script type="text/javascript" src="{{ $prismSecure.Permalink }}" integrity="{{ $prismSecure.Data.Integrity }}"></script>
|
||||
<script type="text/javascript" src="{{ $siteSecure.Permalink }}" integrity="{{ $siteSecure.Data.Integrity }}"></script>
|
||||
</body>
|
||||
</html>
|
34
layouts/_default/index.html
Normal file
@@ -0,0 +1,34 @@
|
||||
{{ define "main" }}
|
||||
<div class="container small center">
|
||||
<div>
|
||||
<div class="home-title-wrapper">
|
||||
<div class="home-logo">{{ readFile "static/elephant.svg" | safeHTML }}</div>
|
||||
<h1 class="home-title">PHP Apprentice</h1>
|
||||
<h3 class="home-subtitle">An online book for learning PHP</h3>
|
||||
</div>
|
||||
<p>
|
||||
PHP Apprentice is an online, open source book about the PHP programming language. PHP is one of the most popular platforms for building websites and web services. It is a great language that is easy to learn and allows you to build powerful and complex web applications very quickly.
|
||||
</p>
|
||||
<p>
|
||||
The goal of PHP Apprentice is to be an easy to understand resource for learning how to write good code in PHP. There are a lot of PHP tutorials on the internet that use outdated practices or insecure code. I want this book to show how to write PHP code with quality.
|
||||
</p>
|
||||
<p>
|
||||
The contents of PHP Apprentice are for beginners and experienced PHP developers. The book currently has content for learning the basics of the language. In the future, more pages will be added for more advanced topics like building websites, database integration and security.
|
||||
</p>
|
||||
<p>
|
||||
PHP Apprentice is currently a work in progress. If you would like to give feedback or request a certain discussion topic, check out the <a href="https://github.com/restoreddev/phpapprentice" target="_blank">GitHub repository</a>.
|
||||
</p>
|
||||
<div class="home-buttons">
|
||||
<a href="/basics.html" class="button">
|
||||
<div class="icon">{{ readFile "static/book-reference.svg" | safeHTML }}</div>
|
||||
Open First Chapter
|
||||
</a>
|
||||
{{ partial "menu_button.html" . }}
|
||||
</div>
|
||||
<hr />
|
||||
<p>Created and managed by <a href="https://twitter.com/restoreddev" target="_blank">Andrew Davis @restoreddev</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ partial "menu_modal.html" . }}
|
||||
{{ end }}
|
26
layouts/_default/single.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{{ define "main" }}
|
||||
<div class="container small center">
|
||||
{{ partial "menu_button.html" . }}
|
||||
|
||||
<h1>{{ .Title }}</h1>
|
||||
<h2 class="subtitle">{{ .Description }}</h2>
|
||||
{{ .Content }}
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div class="navigate-links">
|
||||
{{ if .Params.previous }}
|
||||
<a href="/{{ .Params.previous }}" title="Previous" id="prev-link" class="prev-link">
|
||||
<div class="icon">{{ readFile "static/cheveron-outline-left.svg" | safeHTML }}</div>
|
||||
Previous
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ if .Params.next }}
|
||||
<a href="/{{ .Params.next }}" title="Next" id="next-link" class="next-link">
|
||||
Next
|
||||
<div class="icon">{{ readFile "static/cheveron-outline-right.svg" | safeHTML }}</div>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ partial "menu_modal.html" . }}
|
||||
{{ end }}
|
10
layouts/partials/menu_button.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<div class="menu">
|
||||
<button class="menu-button" title="Open Menu">
|
||||
<div class="icon">{{ readFile "static/show-sidebar.svg" | safeHTML }}</div>
|
||||
Table of Contents
|
||||
</button>
|
||||
<form class="dark-mode-form">
|
||||
<label for="dark_mode">Dark Mode?</label>
|
||||
<input type="checkbox" id="dark_mode" name="dark_mode" />
|
||||
</form>
|
||||
</div>
|
@@ -1,8 +1,8 @@
|
||||
<div class="modal closed">
|
||||
<div class="modal-content">
|
||||
<button class="modal-button right" title="Close">
|
||||
<div class="icon"><?= icon('close-outline') ?></div>
|
||||
<div class="icon">{{ readFile "static/close-outline.svg" | safeHTML }}</div>
|
||||
</button>
|
||||
<?php partial('table_of_contents') ?>
|
||||
{{ partial "table_of_contents.html" . }}
|
||||
</div>
|
||||
</div>
|
39
layouts/partials/table_of_contents.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<div class="table-of-contents">
|
||||
<h4>Table of Contents</h4>
|
||||
<ul class="list-plain">
|
||||
<li><a href="/">Preface</a></li>
|
||||
<li><a href="/installing-php.html">Installing PHP</a></li>
|
||||
</ul>
|
||||
|
||||
<h5 class="section-title">Basics</h5>
|
||||
<ol>
|
||||
<li><a href="/basics.html">Basics</a></li>
|
||||
<li><a href="/variables.html">Variables</a></li>
|
||||
<li><a href="/arithmetic.html">Arithmetic</a></li>
|
||||
<li><a href="/strings.html">Strings</a></li>
|
||||
<li><a href="/comparisons.html">Comparisons</a></li>
|
||||
<li><a href="/boolean-logic.html">Boolean Logic</a></li>
|
||||
<li><a href="/conditionals.html">Conditionals</a></li>
|
||||
<li><a href="/loops.html">Loops</a></li>
|
||||
<li><a href="/arrays.html">Arrays</a></li>
|
||||
<li><a href="/functions.html">Functions</a></li>
|
||||
<li><a href="/classes.html">Classes</a></li>
|
||||
<li><a href="/classes-inheritance.html">Classes: Inheritance</a></li>
|
||||
<li><a href="/classes-visibility.html">Classes: Visibility</a></li>
|
||||
<li><a href="/classes-constructor.html">Classes: Constructor</a></li>
|
||||
<li><a href="/static.html">Static</a></li>
|
||||
<li><a href="/interfaces.html">Interfaces</a></li>
|
||||
<li><a href="/abstract.html">Abstract Classes</a></li>
|
||||
<li><a href="/exceptions.html">Exceptions</a></li>
|
||||
</ol>
|
||||
|
||||
<h5 class="section-title">Web</h5>
|
||||
<ol>
|
||||
<li><a href="/web/http.html">HTTP</a></li>
|
||||
<li><a href="/web/http-post.html">HTTP POST</a></li>
|
||||
<li><a href="/web/http-server.html">PHP HTTP Server</a></li>
|
||||
<li><a href="/web/php-html.html">PHP HTML</a></li>
|
||||
</ol>
|
||||
|
||||
<a href="/credits.html">Credits</a>
|
||||
</div>
|
11472
package-lock.json
generated
18
package.json
@@ -3,10 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"description": "A website for learning PHP",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "./node_modules/.bin/encore dev",
|
||||
"watch": "./node_modules/.bin/encore dev --watch",
|
||||
"prod": "./node_modules/.bin/encore production"
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -19,14 +16,9 @@
|
||||
},
|
||||
"homepage": "https://github.com/restoreddev/phpapprentice#readme",
|
||||
"devDependencies": {
|
||||
"@symfony/webpack-encore": "^0.22.4",
|
||||
"autoprefixer": "^8.6.5",
|
||||
"postcss-import": "^11.1.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-simple-vars": "^4.1.0"
|
||||
"autoprefixer": "^10.4.8",
|
||||
"postcss-cli": "^10.0.0",
|
||||
"postcss-custom-properties": "^12.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-plugin-prismjs": "^1.0.2",
|
||||
"prismjs": "^1.15.0"
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
|
15
phpunit.xml
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./test</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
@@ -1,7 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('postcss-import'),
|
||||
require('postcss-simple-vars'),
|
||||
require('postcss-custom-properties'),
|
||||
require('autoprefixer'),
|
||||
]
|
||||
}
|
||||
|
22
src/App.php
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Apprentice;
|
||||
|
||||
use Apprentice\Command;
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
class App
|
||||
{
|
||||
/**
|
||||
* Runs cli application
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function main(): void
|
||||
{
|
||||
$cli = new Application;
|
||||
$cli->add(new Command\BuildCommand);
|
||||
$cli->add(new Command\ServerCommand);
|
||||
$cli->run();
|
||||
}
|
||||
}
|
150
src/Build.php
@@ -1,150 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Apprentice;
|
||||
|
||||
use Parsedown;
|
||||
|
||||
/**
|
||||
* Handles building pages in public folder
|
||||
* using configuration based on Page classes
|
||||
*/
|
||||
class Build
|
||||
{
|
||||
/**
|
||||
* Builds all pages in config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function buildAll(): void
|
||||
{
|
||||
$this->createOutputFolder();
|
||||
$this->cleanOutputFolder();
|
||||
$this->copyFiles();
|
||||
|
||||
$pages = config('pages');
|
||||
foreach ($pages as $page) {
|
||||
$this->buildPage($page);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds single page in config
|
||||
*
|
||||
* @param string $name Name of page to build
|
||||
* @return string Contents of built page
|
||||
*/
|
||||
public function runSingleBuild(string $name): string
|
||||
{
|
||||
$this->createOutputFolder();
|
||||
|
||||
$pages = config('pages');
|
||||
|
||||
foreach ($pages as $page) {
|
||||
if ($page->name == $name) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->buildPage($page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all html files in public folder
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function cleanOutputFolder(): void
|
||||
{
|
||||
$files = glob(config('output_dir') . '/*.html');
|
||||
foreach ($files as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
private function copyFiles(): void
|
||||
{
|
||||
$filesDir = config('files_dir');
|
||||
if (!file_exists($filesDir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$outputDir = config('output_dir');
|
||||
foreach (glob($filesDir . '/*') as $file) {
|
||||
$name = basename($file);
|
||||
|
||||
copy($file, $outputDir . '/' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates output folder if it does not exist
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function createOutputFolder(): void
|
||||
{
|
||||
if (!file_exists(config('output_dir'))) {
|
||||
mkdir(config('output_dir'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds single Page into html and
|
||||
* outputs to public directory
|
||||
*
|
||||
* @param Page $page Page from config to be built
|
||||
* @return void Contents of built page
|
||||
*/
|
||||
private function buildPage(Page $page): string
|
||||
{
|
||||
if (!empty($page->chapter)) {
|
||||
if (!file_exists(config('chapter_dir') . '/' . $page->chapter)) {
|
||||
throw new \Exception('Code file not found: ' . $page->chapter);
|
||||
}
|
||||
|
||||
$parser = new Parsedown();
|
||||
|
||||
$content = file_get_contents(config('chapter_dir') . '/' . $page->chapter);
|
||||
$page->variables['chapter'] = $parser->text($content);
|
||||
}
|
||||
|
||||
if ($page->template) {
|
||||
$template = config('templates_dir') . '/' . $page->template;
|
||||
} else {
|
||||
$template = config('templates_dir') . '/default.phtml';
|
||||
}
|
||||
$output = $this->getOutput($template, $page->variables);
|
||||
|
||||
$path = config('output_dir') . '/' . $page->name . '.html';
|
||||
$dir = pathinfo($path, PATHINFO_DIRNAME);
|
||||
|
||||
if (!file_exists($dir)) {
|
||||
mkdir($dir, 0777, true);
|
||||
}
|
||||
|
||||
file_put_contents($path, $output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads template file with scoped
|
||||
* variables
|
||||
*
|
||||
* @param string $path Path to template in templates folder
|
||||
* @param array $vars Array of variables to be loaded in template
|
||||
* @return string Output of template
|
||||
*/
|
||||
private function getOutput(string $path, array $vars = []): string {
|
||||
if ($vars) {
|
||||
extract($vars);
|
||||
}
|
||||
|
||||
ob_start();
|
||||
require $path;
|
||||
$output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Apprentice\Command;
|
||||
|
||||
use Apprentice\Build;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class BuildCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('build')
|
||||
->setDescription('Builds PHP templates into HTML files.')
|
||||
->setHelp('Help!');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$build = new Build;
|
||||
$build->buildAll();
|
||||
}
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Apprentice\Command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ServerCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('server')
|
||||
->setDescription('Runs PHP server that automatically builds files')
|
||||
->setHelp('Help!');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$output->writeln('Starting development server on localhost:8080');
|
||||
system('php -S localhost:8080 -t ' . config('output_dir') . ' src/util/router.php');
|
||||
}
|
||||
}
|
77
src/Page.php
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Apprentice;
|
||||
|
||||
/**
|
||||
* A class representing a single page in the build process
|
||||
*/
|
||||
class Page
|
||||
{
|
||||
/**
|
||||
* Name of page, used in filename
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Name of template to use
|
||||
* If none is provided, a default will be used
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $template;
|
||||
|
||||
/**
|
||||
* Name of chapter file to load and parse
|
||||
* Will be skipped if none is provided
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $chapter;
|
||||
|
||||
/**
|
||||
* Map of data to passed to template
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $variables;
|
||||
|
||||
/**
|
||||
* Creates new page
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $template
|
||||
* @param string|null $chapter
|
||||
* @param array $variables
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
?string $chapter = null,
|
||||
?array $variables = [],
|
||||
?string $template = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->chapter = $chapter;
|
||||
$this->variables = $variables;
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static constructor
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $template
|
||||
* @param string|null $code
|
||||
* @param array $variables
|
||||
* @return Apprentice\Page
|
||||
*/
|
||||
public static function create(
|
||||
string $name,
|
||||
?string $chapter = null,
|
||||
?array $variables = [],
|
||||
?string $template = null
|
||||
): Page {
|
||||
return new static($name, $chapter, $variables, $template);
|
||||
}
|
||||
}
|
@@ -1,111 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Runs a scoped require on a partial
|
||||
*
|
||||
* The filename must start with an underscore
|
||||
* and end with .php or .phtml
|
||||
*
|
||||
* @param string $path Name of partial
|
||||
* @param array $vars Variables to use for the partial
|
||||
* @return void
|
||||
*/
|
||||
function partial(string $path, array $vars = []) {
|
||||
$dir = config('templates_dir');
|
||||
if (file_exists($dir. '/' . "_$path.php")) {
|
||||
$file = $dir. '/' . "_$path.php";
|
||||
} elseif (file_exists($dir . '/' . "_$path.phtml")) {
|
||||
$file = $dir. '/' . "_$path.phtml";
|
||||
} else {
|
||||
throw new Exception('Partial could not be found: ' . $dir . '/' . "_$path.php");
|
||||
}
|
||||
|
||||
extract($vars);
|
||||
|
||||
require $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets url path to page
|
||||
*
|
||||
* @param string $page Name of page
|
||||
* @return string
|
||||
*/
|
||||
function page_path(string $page): string {
|
||||
return '/' . $page . '.html';
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely escapes text for display in html
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
function escape(string $text): string {
|
||||
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads content of svg icon from assets folder
|
||||
*
|
||||
* @param string $name Name of icon without extension
|
||||
* @return string Contents of file
|
||||
*/
|
||||
function icon(string $name): string {
|
||||
$dir = config('icon_dir');
|
||||
$path = $dir . '/' . $name . '.svg';
|
||||
if (file_exists($path)) {
|
||||
return file_get_contents($path);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads config file into a global
|
||||
*
|
||||
* @param string $path
|
||||
* @return void
|
||||
*/
|
||||
function load_config(string $path) {
|
||||
$config = require $path;
|
||||
|
||||
if (!is_array($config)) {
|
||||
throw new \Exception('Config file does not return an array.');
|
||||
}
|
||||
|
||||
$GLOBALS['APPRENTICE_CONFIG'] = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config value
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
function config(string $key) {
|
||||
$config = $GLOBALS['APPRENTICE_CONFIG'] ?? [];
|
||||
|
||||
return $config[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to asset based on manifes.json file
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
function asset(string $name): string {
|
||||
$outputDir = config('output_dir');
|
||||
|
||||
if (file_exists($outputDir . '/manifest.json')) {
|
||||
$text = file_get_contents($outputDir . '/manifest.json');
|
||||
$paths = json_decode($text, true);
|
||||
|
||||
if (isset($paths[$name])) {
|
||||
return $paths[$name];
|
||||
}
|
||||
}
|
||||
|
||||
return '/' . $name;
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Router file used for development server
|
||||
* to create html files on demand
|
||||
*/
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
load_config(__DIR__ . '/../../config.php');
|
||||
|
||||
$uri = $_SERVER['REQUEST_URI'];
|
||||
$pathinfo = pathinfo($uri);
|
||||
|
||||
if ($pathinfo['dirname'] == '/' && !isset($pathinfo['extension'])) {
|
||||
$pathinfo['extension'] = 'html';
|
||||
$pathinfo['filename'] = 'index';
|
||||
}
|
||||
|
||||
if ($pathinfo['extension'] == 'html') {
|
||||
$name = $pathinfo['filename'];
|
||||
if ($pathinfo['dirname'] != '/') {
|
||||
$name = ltrim($pathinfo['dirname'], '/') . '/' . $name;
|
||||
}
|
||||
|
||||
$build = new Apprentice\Build;
|
||||
$output = $build->runSingleBuild($name);
|
||||
|
||||
echo $output;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
Before Width: | Height: | Size: 167 B After Width: | Height: | Size: 167 B |
Before Width: | Height: | Size: 169 B After Width: | Height: | Size: 169 B |
Before Width: | Height: | Size: 167 B After Width: | Height: | Size: 167 B |
Before Width: | Height: | Size: 169 B After Width: | Height: | Size: 169 B |
Before Width: | Height: | Size: 200 B After Width: | Height: | Size: 200 B |
Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 225 B |
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 223 B |
Before Width: | Height: | Size: 221 B After Width: | Height: | Size: 221 B |
Before Width: | Height: | Size: 222 B After Width: | Height: | Size: 222 B |
Before Width: | Height: | Size: 314 B After Width: | Height: | Size: 314 B |
Before Width: | Height: | Size: 260 B After Width: | Height: | Size: 260 B |
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 223 B |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 162 B After Width: | Height: | Size: 162 B |
Before Width: | Height: | Size: 121 B After Width: | Height: | Size: 121 B |
Before Width: | Height: | Size: 217 B After Width: | Height: | Size: 217 B |
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Test;
|
||||
|
||||
class BaseTestCase extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Test;
|
||||
|
||||
use Apprentice\Build;
|
||||
|
||||
class BuildTest extends BaseTestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
load_config(__DIR__ . '/static/config.php');
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
$files = glob('/tmp/apprentice_output/*');
|
||||
foreach ($files as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
rmdir('/tmp/apprentice_output');
|
||||
}
|
||||
|
||||
public function test_build_single_page()
|
||||
{
|
||||
$build = new Build();
|
||||
|
||||
$build->runSingleBuild('test');
|
||||
|
||||
$html = file_get_contents('/tmp/apprentice_output/test.html');
|
||||
$expectedHtml = "<div>Test Title</div>\n" .
|
||||
"<div>Test Subtitle</div>\n" .
|
||||
"<div>Test Description</div>\n" .
|
||||
"<div><p>Test comment</p>\n" .
|
||||
"<pre><code class="language-php">\$test = 'test';</code></pre></div>\n";
|
||||
|
||||
$this->assertFalse(empty($html));
|
||||
$this->assertEquals($expectedHtml, $html);
|
||||
}
|
||||
|
||||
public function test_build_all()
|
||||
{
|
||||
$build = new Build();
|
||||
|
||||
$build->buildAll();
|
||||
|
||||
$expectedHtml = "<div>Test Title</div>\n" .
|
||||
"<div>Test Subtitle</div>\n" .
|
||||
"<div>Test Description</div>\n" .
|
||||
"<div><p>Test comment</p>\n" .
|
||||
"<pre><code class="language-php">\$test = 'test';</code></pre></div>\n";
|
||||
$expectedHtml2 = "<div>index</div>\n";
|
||||
|
||||
$html = file_get_contents('/tmp/apprentice_output/test.html');
|
||||
$html2 = file_get_contents('/tmp/apprentice_output/index.html');
|
||||
|
||||
$this->assertEquals($expectedHtml, $html);
|
||||
$this->assertEquals($expectedHtml2, $html2);
|
||||
$this->assertTrue(file_exists('/tmp/apprentice_output/test.txt'));
|
||||
$this->assertTrue(file_exists('/tmp/apprentice_output/TEST'));
|
||||
}
|
||||
}
|
@@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Test;
|
||||
|
||||
class FunctionsTest extends BaseTestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
mkdir('/tmp/apprentice_output');
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
$GLOBALS['PARTIAL_TEST'] = null;
|
||||
|
||||
$files = glob('/tmp/apprentice_output/*');
|
||||
foreach ($files as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
rmdir('/tmp/apprentice_output');
|
||||
}
|
||||
|
||||
public function test_load_config()
|
||||
{
|
||||
load_config(__DIR__ . '/static/config.php');
|
||||
|
||||
$this->assertEquals(__DIR__ . '/static/../icons', config('icon_dir'));
|
||||
}
|
||||
|
||||
public function test_page_path()
|
||||
{
|
||||
$page = page_path('home');
|
||||
|
||||
$this->assertEquals('/home.html', $page);
|
||||
}
|
||||
|
||||
public function test_escape()
|
||||
{
|
||||
$output = escape("'\"&");
|
||||
|
||||
$this->assertEquals("'"&", $output);
|
||||
}
|
||||
|
||||
public function test_icon()
|
||||
{
|
||||
load_config(__DIR__ . '/static/config.php');
|
||||
$icon = icon('test');
|
||||
|
||||
$this->assertFalse(empty($icon));
|
||||
$this->assertEquals("<test></test>\n", $icon);
|
||||
}
|
||||
|
||||
public function test_partial()
|
||||
{
|
||||
partial('partial', ['test' => 'test var']);
|
||||
|
||||
$this->assertEquals('test var', $GLOBALS['PARTIAL_TEST']);
|
||||
}
|
||||
|
||||
public function test_asset_path_with_manifest()
|
||||
{
|
||||
file_put_contents(
|
||||
'/tmp/apprentice_output/manifest.json',
|
||||
json_encode(['js/app.js' => '/js/app-1234.js'])
|
||||
);
|
||||
|
||||
$path = asset('js/app.js');
|
||||
|
||||
$this->assertEquals('/js/app-1234.js', $path);
|
||||
}
|
||||
|
||||
public function test_asset_path_without_manifest()
|
||||
{
|
||||
$path = asset('js/app.js');
|
||||
|
||||
$this->assertEquals('/js/app.js', $path);
|
||||
}
|
||||
}
|
@@ -1 +0,0 @@
|
||||
<test></test>
|
@@ -1,4 +0,0 @@
|
||||
Test comment
|
||||
```php
|
||||
$test = 'test';
|
||||
```
|
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Apprentice\Page;
|
||||
|
||||
return [
|
||||
'icon_dir' => __DIR__ . '/../icons',
|
||||
'chapter_dir' => __DIR__ . '/chapter',
|
||||
'templates_dir' => __DIR__ . '/templates',
|
||||
'output_dir' => '/tmp/apprentice_output',
|
||||
'files_dir' => __DIR__ . '/files',
|
||||
'pages' => [
|
||||
Page::create('index', null, [], 'index.phtml'),
|
||||
Page::create('test', 'test.md', [
|
||||
'title' => 'Test Title',
|
||||
'subtitle' => 'Test Subtitle',
|
||||
'description' => 'Test Description',
|
||||
]),
|
||||
],
|
||||
];
|
@@ -1,3 +0,0 @@
|
||||
<?php
|
||||
|
||||
$GLOBALS['PARTIAL_TEST'] = $test;
|
@@ -1,4 +0,0 @@
|
||||
<div><?= escape($title) ?></div>
|
||||
<div><?= escape($subtitle) ?></div>
|
||||
<div><?= escape($description) ?></div>
|
||||
<div><?= escape($chapter) ?></div>
|
@@ -1 +0,0 @@
|
||||
<div>index</div>
|
@@ -1,18 +0,0 @@
|
||||
var Encore = require('@symfony/webpack-encore');
|
||||
|
||||
Encore
|
||||
.setOutputPath('.build')
|
||||
.setPublicPath('/')
|
||||
.addEntry('js/site', './assets/js/site.js')
|
||||
.addStyleEntry('css/site', './assets/css/site.css')
|
||||
.enablePostCssLoader()
|
||||
.configureBabel(function (babel) {
|
||||
babel.plugins.push(['prismjs', {
|
||||
"languages": ["php", "http"],
|
||||
"css": false,
|
||||
}]);
|
||||
})
|
||||
.enableVersioning();
|
||||
;
|
||||
|
||||
module.exports = Encore.getWebpackConfig();
|