Created initial site setup with hugo
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,8 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
load_config(__DIR__ . '/config.php');
|
||||
|
||||
$app = new Apprentice\App;
|
||||
$app->main();
|
@@ -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>
|
153
assets/prism.css
Normal file
@@ -0,0 +1,153 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
code[class*="language-"],
|
||||
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;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||
background: #073642; /* base02 */
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||
background: #073642; /* base02 */
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background-color: #fdf6e3; /* base3 */
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #93a1a1; /* base1 */
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #586e75; /* base01 */
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #268bd2; /* blue */
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.url,
|
||||
.token.inserted {
|
||||
color: #2aa198; /* cyan */
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
color: #657b83; /* base00 */
|
||||
background: #eee8d5; /* base2 */
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #859900; /* green */
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #b58900; /* yellow */
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #cb4b16; /* orange */
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
10
assets/prism.js
Normal file
@@ -1,4 +1,4 @@
|
||||
@import "prismjs/themes/prism-solarizedlight.css";
|
||||
@import "assets/prism.css";
|
||||
|
||||
$primary-color: #2AA198;
|
||||
$primary-color-dark: #1D6E68;
|
@@ -1,8 +1,4 @@
|
||||
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
|
@@ -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
4
config.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
baseURL = "https://phpapprentice.com/"
|
||||
languageCode = "en-us"
|
||||
title = "PHP Apprentice"
|
||||
uglyurls = true
|
@@ -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
|
7
layouts/404.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<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>
|
24
layouts/_default/baseof.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!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">
|
||||
|
||||
{{ $css := resources.Get "site.css" }}
|
||||
{{ $style := $css | resources.PostCSS }}
|
||||
<link rel="stylesheet" type="text/css" href="{{ $style.Permalink }}">
|
||||
<link rel="icon" href="/favicon-32.png">
|
||||
|
||||
{{ $prism := resources.Get "prism.js" }}
|
||||
{{ $site := resources.Get "site.js"}}
|
||||
{{ $js := slice $prism $site | resources.Concat "bundle.js" }}
|
||||
<script src="{{ $js.Permalink }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
{{ block "main" . }}{{ end }}
|
||||
</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>
|
||||
<h3 class="subtitle">{{ .Description }}</h3>
|
||||
{{ .Content }}
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div class="navigate-links">
|
||||
{{ if .Params.previous }}
|
||||
<a href="/{{ .Params.previous }}" title="Previous" id="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">
|
||||
Next
|
||||
<div class="icon">{{ readFile "static/cheveron-outline-right.svg" | safeHTML }}</div>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ partial "menu_modal.html" . }}
|
||||
{{ end }}
|
6
layouts/partials/menu_button.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<div class="menu">
|
||||
<button class="menu-button" title="Open Menu">
|
||||
<div class="icon">{{ readFile "static/show-sidebar.svg" | safeHTML }}</div>
|
||||
Table of Contents
|
||||
</button>
|
||||
</div>
|
8
layouts/partials/menu_modal.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<div class="modal closed">
|
||||
<div class="modal-content">
|
||||
<button class="modal-button right" title="Close">
|
||||
<div class="icon">{{ readFile "static/close-outline.svg" | safeHTML }}</div>
|
||||
</button>
|
||||
{{ partial "table_of_contents.html" . }}
|
||||
</div>
|
||||
</div>
|
38
layouts/partials/table_of_contents.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<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>
|
||||
</ol>
|
||||
|
||||
<a href="/credits.html">Credits</a>
|
||||
</div>
|
8504
package-lock.json
generated
19
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,10 @@
|
||||
},
|
||||
"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": "^9.6.1",
|
||||
"postcss-cli": "^6.1.3",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-simple-vars": "^5.0.2"
|
||||
},
|
||||
"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>
|
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();
|