├── .gitignore ├── LICENSE.md ├── README.md ├── archetypes └── default.md ├── assets ├── dark-mode.js ├── prism-solarized.css ├── prism-tomorrow-night.css ├── prism.js ├── site.js └── styles.css ├── config.toml ├── content ├── 01-basics.md ├── 02-variables.md ├── 03-arithmetic.md ├── 04-strings.md ├── 05-comparisons.md ├── 06-boolean-logic.md ├── 07-conditionals.md ├── 08-loops.md ├── 09-arrays.md ├── 10-functions.md ├── 11-classes.md ├── 12-classes-inheritance.md ├── 13-classes-visibility.md ├── 14-classes-constructor.md ├── 15-static.md ├── 16-interfaces.md ├── 17-abstract.md ├── 18-exceptions.md ├── _index.md ├── credits.html ├── installing-php.html └── web │ ├── 01-http.md │ ├── 02-http-post.md │ ├── 03-http-server.md │ └── 04-php-html.md ├── layouts ├── 404.html ├── _default │ ├── baseof.html │ ├── index.html │ ├── list.html │ └── single.html └── partials │ ├── menu_button.html │ ├── menu_modal.html │ └── table_of_contents.html ├── package-lock.json ├── package.json ├── postcss.config.js └── static ├── arrow-thin-down.svg ├── arrow-thin-left.svg ├── arrow-thin-right.svg ├── arrow-thin-up.svg ├── book-reference.svg ├── cheveron-outline-down.svg ├── cheveron-outline-left.svg ├── cheveron-outline-right.svg ├── cheveron-outline-up.svg ├── close-outline.svg ├── close-solid.svg ├── close.svg ├── elephant.svg ├── favicon-32.png ├── list-bullet.svg ├── menu.svg └── show-sidebar.svg /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | /public 4 | /node_modules 5 | /resources 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2019 Andrew Davis 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Apprentice 2 | PHP Apprentice is a project for sharing knowledge about the PHP programming language. 3 | It is an online book that currently includes pages for learning the basics of PHP programming. 4 | PHP Apprentice is a work in progress so any constructive feedback is appreciated. 5 | 6 | ## Contributing 7 | 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`. 8 | 9 | ## Roadmap 10 | Moving forward, there are three sections I would like to add to the site: 11 | - Web 12 | - Database 13 | - Advanced 14 | 15 | The web section will outline the basics of HTTP, how to handle requests and response and generating HTML. 16 | 17 | The database section will cover PDO setup, fetching data and securely running queries. 18 | 19 | Advanced will hold deeper PHP topics like traits, closures, Composer, auto-loading and namespaces. 20 | 21 | If you would like to request a topic or see one section before another, feel free to open an issue. 22 | -------------------------------------------------------------------------------- /archetypes/default.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "{{ replace .Name "-" " " | title }}" 3 | description = "" 4 | tags = [] 5 | draft = true 6 | slug = "" 7 | previous = "" 8 | +++ 9 | -------------------------------------------------------------------------------- /assets/dark-mode.js: -------------------------------------------------------------------------------- 1 | var bodyTag = document.querySelector('body'); 2 | if (localStorage.getItem('dark_mode') === 'true') { 3 | bodyTag.className = 'dark-mode'; 4 | } -------------------------------------------------------------------------------- /assets/prism-solarized.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.16.0 2 | https://prismjs.com/download.html#themes=prism-solarizedlight&languages=markup+css+clike+javascript+markup-templating+http+php */ 3 | /* 4 | Solarized Color Schemes originally by Ethan Schoonover 5 | http://ethanschoonover.com/solarized 6 | 7 | Ported for PrismJS by Hector Matos 8 | Website: https://krakendev.io 9 | Twitter Handle: https://twitter.com/allonsykraken) 10 | */ 11 | 12 | /* 13 | SOLARIZED HEX 14 | --------- ------- 15 | base03 #002b36 16 | base02 #073642 17 | base01 #586e75 18 | base00 #657b83 19 | base0 #839496 20 | base1 #93a1a1 21 | base2 #eee8d5 22 | base3 #fdf6e3 23 | yellow #b58900 24 | orange #cb4b16 25 | red #dc322f 26 | magenta #d33682 27 | violet #6c71c4 28 | blue #268bd2 29 | cyan #2aa198 30 | green #859900 31 | */ 32 | 33 | .light-mode code[class*="language-"], 34 | .light-mode pre[class*="language-"] { 35 | color: #657b83; /* base00 */ 36 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 37 | font-size: 1em; 38 | text-align: left; 39 | white-space: pre; 40 | word-spacing: normal; 41 | word-break: normal; 42 | word-wrap: normal; 43 | 44 | line-height: 1.5; 45 | 46 | -moz-tab-size: 4; 47 | -o-tab-size: 4; 48 | tab-size: 4; 49 | 50 | -webkit-hyphens: none; 51 | -moz-hyphens: none; 52 | -ms-hyphens: none; 53 | hyphens: none; 54 | } 55 | 56 | .light-mode pre[class*="language-"]::-moz-selection, 57 | .light-mode pre[class*="language-"] ::-moz-selection, 58 | .light-mode code[class*="language-"]::-moz-selection, 59 | .light-mode code[class*="language-"] ::-moz-selection { 60 | background: #073642; /* base02 */ 61 | } 62 | 63 | .light-mode pre[class*="language-"]::selection, 64 | .light-mode pre[class*="language-"] ::selection, 65 | .light-mode code[class*="language-"]::selection, 66 | .light-mode code[class*="language-"] ::selection { 67 | background: #073642; /* base02 */ 68 | } 69 | 70 | /* Code blocks */ 71 | .light-mode pre[class*="language-"] { 72 | padding: 1em; 73 | margin: .5em 0; 74 | overflow: auto; 75 | border-radius: 0.3em; 76 | } 77 | 78 | .light-mode :not(pre) > code[class*="language-"], 79 | .light-mode pre[class*="language-"] { 80 | background-color: #fdf6e3; /* base3 */ 81 | } 82 | 83 | /* Inline code */ 84 | .light-mode :not(pre) > code[class*="language-"] { 85 | padding: .1em; 86 | border-radius: .3em; 87 | } 88 | 89 | .light-mode .token.comment, 90 | .light-mode .token.prolog, 91 | .light-mode .token.doctype, 92 | .light-mode .token.cdata { 93 | color: #93a1a1; /* base1 */ 94 | } 95 | 96 | .light-mode .token.punctuation { 97 | color: #586e75; /* base01 */ 98 | } 99 | 100 | .light-mode .namespace { 101 | opacity: .7; 102 | } 103 | 104 | .light-mode .token.property, 105 | .light-mode .token.tag, 106 | .light-mode .token.boolean, 107 | .light-mode .token.number, 108 | .light-mode .token.constant, 109 | .light-mode .token.symbol, 110 | .light-mode .token.deleted { 111 | color: #268bd2; /* blue */ 112 | } 113 | 114 | .light-mode .token.selector, 115 | .light-mode .token.attr-name, 116 | .light-mode .token.string, 117 | .light-mode .token.char, 118 | .light-mode .token.builtin, 119 | .light-mode .token.url, 120 | .light-mode .token.inserted { 121 | color: #2aa198; /* cyan */ 122 | } 123 | 124 | .light-mode .token.entity { 125 | color: #657b83; /* base00 */ 126 | background: #eee8d5; /* base2 */ 127 | } 128 | 129 | .light-mode .token.atrule, 130 | .light-mode .token.attr-value, 131 | .light-mode .token.keyword { 132 | color: #859900; /* green */ 133 | } 134 | 135 | .light-mode .token.function, 136 | .light-mode .token.class-name { 137 | color: #b58900; /* yellow */ 138 | } 139 | 140 | .light-mode .token.regex, 141 | .light-mode .token.important, 142 | .light-mode .token.variable { 143 | color: #cb4b16; /* orange */ 144 | } 145 | 146 | .light-mode .token.important, 147 | .light-mode .token.bold { 148 | font-weight: bold; 149 | } 150 | .light-mode .token.italic { 151 | font-style: italic; 152 | } 153 | 154 | .light-mode .token.entity { 155 | cursor: help; 156 | } 157 | 158 | -------------------------------------------------------------------------------- /assets/prism-tomorrow-night.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.19.0 2 | https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */ 3 | /** 4 | * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML 5 | * Based on https://github.com/chriskempson/tomorrow-theme 6 | * @author Rose Pritchard 7 | */ 8 | 9 | .dark-mode code[class*="language-"], 10 | .dark-mode pre[class*="language-"] { 11 | color: #ccc; 12 | background: none; 13 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 14 | font-size: 1em; 15 | text-align: left; 16 | white-space: pre; 17 | word-spacing: normal; 18 | word-break: normal; 19 | word-wrap: normal; 20 | line-height: 1.5; 21 | 22 | -moz-tab-size: 4; 23 | -o-tab-size: 4; 24 | tab-size: 4; 25 | 26 | -webkit-hyphens: none; 27 | -moz-hyphens: none; 28 | -ms-hyphens: none; 29 | hyphens: none; 30 | 31 | } 32 | 33 | /* Code blocks */ 34 | .dark-mode pre[class*="language-"] { 35 | padding: 1em; 36 | margin: .5em 0; 37 | overflow: auto; 38 | } 39 | 40 | .dark-mode :not(pre) > code[class*="language-"], 41 | .dark-mode pre[class*="language-"] { 42 | background: #2d2d2d; 43 | } 44 | 45 | /* Inline code */ 46 | .dark-mode :not(pre) > code[class*="language-"] { 47 | padding: .1em; 48 | border-radius: .3em; 49 | white-space: normal; 50 | } 51 | 52 | .dark-mode .token.comment, 53 | .dark-mode .token.block-comment, 54 | .dark-mode .token.prolog, 55 | .dark-mode .token.doctype, 56 | .dark-mode .token.cdata { 57 | color: #999; 58 | } 59 | 60 | .dark-mod .token.punctuation { 61 | color: #ccc; 62 | } 63 | 64 | .dark-mode .token.tag, 65 | .dark-mode .token.attr-name, 66 | .dark-mode .token.namespace, 67 | .dark-mode .token.deleted { 68 | color: #e2777a; 69 | } 70 | 71 | .dark-mode .token.function-name { 72 | color: #6196cc; 73 | } 74 | 75 | .dark-mode .token.boolean, 76 | .dark-mode .token.number, 77 | .dark-mode .token.function { 78 | color: #f08d49; 79 | } 80 | 81 | .dark-mode .token.property, 82 | .dark-mode .token.class-name, 83 | .dark-mode .token.constant, 84 | .dark-mode .token.symbol { 85 | color: #f8c555; 86 | } 87 | 88 | .dark-mode .token.selector, 89 | .dark-mode .token.important, 90 | .dark-mode .token.atrule, 91 | .dark-mode .token.keyword, 92 | .dark-mode .token.builtin { 93 | color: #cc99cd; 94 | } 95 | 96 | .dark-mode .token.string, 97 | .dark-mode .token.char, 98 | .dark-mode .token.attr-value, 99 | .dark-mode .token.regex, 100 | .dark-mode .token.variable { 101 | color: #7ec699; 102 | } 103 | 104 | .dark-mode .token.operator, 105 | .dark-mode .token.entity, 106 | .dark-mode .token.url { 107 | color: #67cdcc; 108 | } 109 | 110 | .dark-mode .token.important, 111 | .dark-mode .token.bold { 112 | font-weight: bold; 113 | } 114 | .dark-mode .token.italic { 115 | font-style: italic; 116 | } 117 | 118 | .dark-mode .token.entity { 119 | cursor: help; 120 | } 121 | 122 | .dark-mode .token.inserted { 123 | color: green; 124 | } 125 | 126 | -------------------------------------------------------------------------------- /assets/prism.js: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.16.0 2 | https://prismjs.com/download.html#themes=prism-solarizedlight&languages=markup+css+clike+javascript+markup-templating+http+php */ 3 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(g){var c=/\blang(?:uage)?-([\w-]+)\b/i,a=0,C={manual:g.Prism&&g.Prism.manual,disableWorkerMessageHandler:g.Prism&&g.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof M?new M(e.type,C.util.encode(e.content),e.alias):Array.isArray(e)?e.map(C.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(k instanceof M)){if(f&&y!=a.length-1){if(c.lastIndex=v,!(x=c.exec(e)))break;for(var b=x.index+(h?x[1].length:0),w=x.index+x[0].length,A=y,P=v,O=a.length;A"+n.content+""},!g.document)return g.addEventListener&&(C.disableWorkerMessageHandler||g.addEventListener("message",function(e){var a=JSON.parse(e.data),n=a.language,t=a.code,r=a.immediateClose;g.postMessage(C.highlight(t,C.languages[n],n)),r&&g.close()},!1)),C;var e=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return e&&(C.filename=e.src,C.manual||e.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(C.highlightAll):window.setTimeout(C.highlightAll,16):document.addEventListener("DOMContentLoaded",C.highlightAll))),C}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); 4 | Prism.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^$/i;var n={"included-cdata":{pattern://i,inside:s}};n["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var i={};i[a]={pattern:RegExp("(<__[\\s\\S]*?>)(?:\\s*|[\\s\\S])*?(?=<\\/__>)".replace(/__/g,a),"i"),lookbehind:!0,greedy:!0,inside:n},Prism.languages.insertBefore("markup","cdata",i)}}),Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; 5 | !function(s){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/@[\w-]+/}},url:{pattern:RegExp("url\\((?:"+t.source+"|[^\n\r()]*)\\)","i"),inside:{function:/^url/i,punctuation:/^\(|\)$/}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+t.source+")*?(?=\\s*\\{)"),string:{pattern:t,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var e=s.languages.markup;e&&(e.tag.addInlined("style","css"),s.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:e.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:s.languages.css}},alias:"language-css"}},e.tag))}(Prism); 6 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; 7 | Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.])\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|[^\\`])*`/,greedy:!0,inside:{interpolation:{pattern:/\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.js=Prism.languages.javascript; 8 | !function(h){function v(e,n){return"___"+e.toUpperCase()+n+"___"}Object.defineProperties(h.languages["markup-templating"]={},{buildPlaceholders:{value:function(a,r,e,o){if(a.language===r){var c=a.tokenStack=[];a.code=a.code.replace(e,function(e){if("function"==typeof o&&!o(e))return e;for(var n,t=c.length;-1!==a.code.indexOf(n=v(r,t));)++t;return c[t]=e,n}),a.grammar=h.languages.markup}}},tokenizePlaceholders:{value:function(p,k){if(p.language===k&&p.tokenStack){p.grammar=h.languages[k];var m=0,d=Object.keys(p.tokenStack);!function e(n){for(var t=0;t=d.length);t++){var a=n[t];if("string"==typeof a||a.content&&"string"==typeof a.content){var r=d[m],o=p.tokenStack[r],c="string"==typeof a?a:a.content,i=v(k,r),u=c.indexOf(i);if(-1$|^<\?(?:php(?=\s)|=)?/i,alias:"important"}}),n.languages.insertBefore("php","keyword",{variable:/\$+(?:\w+\b|(?={))/i,package:{pattern:/(\\|namespace\s+|use\s+)[\w\\]+/,lookbehind:!0,inside:{punctuation:/\\/}}}),n.languages.insertBefore("php","operator",{property:{pattern:/(->)[\w]+/,lookbehind:!0}});var e={pattern:/{\$(?:{(?:{[^{}]+}|[^{}]+)}|[^{}])+}|(^|[^\\{])\$+(?:\w+(?:\[.+?]|->\w+)*)/,lookbehind:!0,inside:{rest:n.languages.php}};n.languages.insertBefore("php","string",{"nowdoc-string":{pattern:/<<<'([^']+)'(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;/,greedy:!0,alias:"string",inside:{delimiter:{pattern:/^<<<'[^']+'|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<'?|[';]$/}}}},"heredoc-string":{pattern:/<<<(?:"([^"]+)"(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;|([a-z_]\w*)(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\2;)/i,greedy:!0,alias:"string",inside:{delimiter:{pattern:/^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<"?|[";]$/}},interpolation:e}},"single-quoted-string":{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0,alias:"string"},"double-quoted-string":{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,alias:"string",inside:{interpolation:e}}}),delete n.languages.php.string,n.hooks.add("before-tokenize",function(e){if(/<\?/.test(e.code)){n.languages["markup-templating"].buildPlaceholders(e,"php",/<\?(?:[^"'/#]|\/(?![*/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|(?:\/\/|#)(?:[^?\n\r]|\?(?!>))*|\/\*[\s\S]*?(?:\*\/|$))*?(?:\?>|$)/gi)}}),n.hooks.add("after-tokenize",function(e){n.languages["markup-templating"].tokenizePlaceholders(e,"php")})}(Prism); 11 | -------------------------------------------------------------------------------- /assets/site.js: -------------------------------------------------------------------------------- 1 | // Dark mode 2 | var bodyTag = document.querySelector('body'); 3 | var darkMode = document.getElementById('dark_mode'); 4 | if (localStorage.getItem('dark_mode') === 'true') { 5 | darkMode.checked = true; 6 | } else { 7 | darkMode.checked = false; 8 | } 9 | 10 | darkMode.addEventListener('change', function (e) { 11 | if (this.checked) { 12 | localStorage.setItem('dark_mode', true); 13 | bodyTag.className = 'dark-mode'; 14 | } else { 15 | localStorage.setItem('dark_mode', false); 16 | bodyTag.className = 'light-mode'; 17 | } 18 | }); 19 | 20 | // Arrow key bindings 21 | document.onkeydown = function (e) { 22 | e = e || window.event; 23 | 24 | switch (e.key) { 25 | case 'Esc': 26 | case 'Escape': 27 | var modal = document.querySelector('.modal'); 28 | if (modal && !modal.classList.contains('closed')) { 29 | modal.classList.add('closed'); 30 | } 31 | break; 32 | case 'ArrowRight': 33 | var link = document.querySelector('#next-link'); 34 | if (link) { 35 | window.location.href = link.href; 36 | } 37 | break; 38 | case 'ArrowLeft': 39 | var link = document.querySelector('#prev-link'); 40 | if (link) { 41 | window.location.href = link.href; 42 | } 43 | break; 44 | } 45 | }; 46 | 47 | // Table of contents 48 | var menuButton = document.querySelector('.menu-button'); 49 | if (!menuButton) { 50 | throw new Error('No menu button'); 51 | } 52 | 53 | var modalButton = document.querySelector('.modal-button'); 54 | var modal = document.querySelector('.modal'); 55 | var modalContent = document.querySelector('.modal-content'); 56 | 57 | var clickEvent = function (e) { 58 | var modal = document.querySelector('.modal'); 59 | 60 | if (modal.classList.contains('closed')) { 61 | modal.classList.remove('closed') 62 | } else { 63 | modal.classList.add('closed') 64 | } 65 | }; 66 | menuButton.addEventListener('click', clickEvent); 67 | modalButton.addEventListener('click', clickEvent); 68 | 69 | modal.addEventListener('click', function (e) { 70 | var target = e.target 71 | do { 72 | if (target == modalContent) { 73 | return; 74 | } 75 | target = target.parentNode; 76 | } while (target); 77 | 78 | modal.classList.add('closed') 79 | }); 80 | 81 | 82 | -------------------------------------------------------------------------------- /assets/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary-color: #2AA198; 3 | --primary-color-dark: #1D6E68; 4 | --drop-shadow: rgba(0, 0, 0, 0.4); 5 | --code-highlight: #EDEDED; 6 | --modal-background: #FFF; 7 | --button-text: #FFF; 8 | --hr-color: #EEE; 9 | --dark-background: #222; 10 | --dark-text-color: #CCC; 11 | --dark-primary-color: #cc99cd; /*#7ec699;*/ 12 | --dark-primary-color-dark: #8C698C; /*#568769;*/ 13 | --dark-button-text: #222; 14 | --dark-code-highlight: #111; 15 | --dark-modal-background: #222; 16 | --dark-drop-shadow: rgba(0, 0, 0, 0.6); 17 | --dark-hr-color: #444; 18 | } 19 | 20 | body { 21 | font-family: Cambria, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "Bitstream Charter", "Caladea", "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif; 22 | font-size: 16px; 23 | padding-top: 1em; 24 | padding-bottom: 1em; 25 | } 26 | body.dark-mode { 27 | background-color: var(--dark-background); 28 | color: var(--dark-text-color); 29 | } 30 | button { 31 | font-family: Cambria, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "Bitstream Charter", "Caladea", "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif; 32 | } 33 | code { 34 | background-color: var(--code-highlight); 35 | font-family: Menlo, Monaco, Consolas, "Ubuntu Mono", "Noto Mono", "Liberation Mono", "Courier New", monospace !important; 36 | font-variant-ligatures: none; 37 | font-size: 14px !important; 38 | padding: 0.10em 0.25em; 39 | } 40 | .dark-mode code { 41 | background-color: var(--dark-code-highlight); 42 | } 43 | pre, pre code { 44 | background-color: transparent; 45 | font-family: Menlo, Monaco, Consolas, "Ubuntu Mono", "Noto Mono", "Liberation Mono", "Courier New", monospace !important; 46 | font-variant-ligatures: none; 47 | font-size: 14px !important; 48 | margin: 0 !important; 49 | border-radius: 0 !important; 50 | padding: 0.5em 0 0.5em 0; 51 | } 52 | a { 53 | color: var(--primary-color); 54 | text-decoration: none; 55 | } 56 | .dark-mode a { 57 | color: var(--dark-primary-color); 58 | } 59 | a:hover { 60 | text-decoration: underline; 61 | color: var(--primary-color-dark); 62 | } 63 | .dark-mode a:hover { 64 | color: var(--dark-primary-color-dark); 65 | } 66 | hr { 67 | border: 0; 68 | height: 1px; 69 | background-color: var(--hr-color); 70 | margin: 0.5em 0; 71 | } 72 | .dark-mode hr { 73 | background-color: var(--dark-hr-color); 74 | } 75 | p { 76 | line-height: 1.5; 77 | margin-top: 1.5em; 78 | margin-bottom: 0.75em; 79 | } 80 | ol { 81 | padding-left: 1em; 82 | } 83 | h1, h2, h3, h4 { 84 | margin: 1.414em 0 0.5em; 85 | font-weight: inherit; 86 | line-height: 1.2; 87 | } 88 | h1 { 89 | margin-top: 0; 90 | font-size: 2.441em; 91 | } 92 | h2 {font-size: 1.953em;} 93 | h3 {font-size: 1.563em;} 94 | h4 {font-size: 1.25em;} 95 | small, .font_small {font-size: 0.8em;} 96 | button { 97 | font-size: 1em; 98 | background-color: transparent; 99 | border: 0; 100 | cursor: pointer; 101 | padding: 0; 102 | color: var(--primary-color); 103 | } 104 | .dark-mode button { 105 | color: var(--dark-primary-color); 106 | } 107 | button:hover { 108 | color: var(--primary-color-dark); 109 | } 110 | .dark-mode button:hover { 111 | color: var(--dark-primary-color-dark); 112 | } 113 | button:focus { 114 | outline: 0; 115 | } 116 | button .icon { 117 | vertical-align: middle; 118 | } 119 | button .icon svg { 120 | fill: var(--primary-color); 121 | } 122 | .dark-mode button .icon svg { 123 | fill: var(--dark-primary-color); 124 | } 125 | button:hover .icon svg { 126 | fill: var(--primary-color-dark); 127 | } 128 | .dark-mode button:hover .icon svg { 129 | fill: var(--dark-primary-color-dark); 130 | } 131 | .button { 132 | background-color: var(--primary-color); 133 | color: var(--button-text); 134 | padding: 0.5em 0.75em; 135 | border-radius: 2em; 136 | display: inline-block; 137 | } 138 | .dark-mode .button { 139 | color: var(--dark-button-text); 140 | background-color: var(--dark-primary-color); 141 | } 142 | .button:hover { 143 | color: var(--button-text); 144 | background-color: var(--primary-color-dark); 145 | text-decoration: none; 146 | } 147 | .dark-mode .button:hover { 148 | color: var(--dark-button-text); 149 | background-color: var(--dark-primary-color-dark); 150 | } 151 | .button .icon { 152 | vertical-align: middle; 153 | } 154 | .button .icon svg { 155 | fill: var(--button-text); 156 | } 157 | .dark-mode .button .icon svg { 158 | fill: var(--dark-button-text); 159 | } 160 | .clearfix:after { 161 | content: ""; 162 | display: table; 163 | clear: both; 164 | } 165 | .container { 166 | max-width: 1050px; 167 | } 168 | .container.small { 169 | max-width: 750px; 170 | } 171 | .center { 172 | margin-left: auto; 173 | margin-right: auto; 174 | } 175 | .right { 176 | float: right; 177 | } 178 | .doc { 179 | padding: 0.5em 0; 180 | line-height: 1.2; 181 | } 182 | .subtitle { 183 | margin-top: 0; 184 | } 185 | .description { 186 | max-width: 25em; 187 | } 188 | .list-plain { 189 | list-style: none; 190 | padding-left: 0; 191 | } 192 | .body-ol li { 193 | padding: 0.5em 0; 194 | } 195 | .navigate-links { 196 | margin: 1em 0; 197 | float: right; 198 | } 199 | .navigate-links .icon { 200 | vertical-align: middle; 201 | } 202 | .navigate-links .icon svg { 203 | fill: var(--primary-color); 204 | } 205 | .dark-mode .navigate-links .icon svg { 206 | fill: var(--dark-primary-color); 207 | } 208 | .prev-link:hover .icon svg, 209 | .next-link:hover .icon svg { 210 | fill: var(--primary-color-dark); 211 | } 212 | .dark-mode .prev-link:hover .icon svg, 213 | .dark-mode .next-link:hover .icon svg { 214 | fill: var(--dark-primary-color-dark); 215 | } 216 | .navigate-links a { 217 | margin-left: 1em; 218 | } 219 | .navigate-links a:hover { 220 | text-decoration: none; 221 | } 222 | .table-of-contents h4 { 223 | margin-top: 0; 224 | } 225 | .table-of-contents ol { 226 | margin: 1em 0; 227 | } 228 | .table-of-contents .section-title { 229 | font-weight: bold; 230 | font-size: 0.8em; 231 | margin-top: 1em; 232 | } 233 | .icon { 234 | width: 1em; 235 | height: 1.25em; 236 | display: inline-block; 237 | } 238 | .dark-mode-form { 239 | float: right; 240 | padding-left: 1rem; 241 | } 242 | .home-title { 243 | text-align: center; 244 | } 245 | .home-subtitle { 246 | text-align: center; 247 | margin-top: 0; 248 | grid-column-start: 2; 249 | grid-column-end: 3; 250 | } 251 | .home-logo { 252 | display: block; 253 | margin: 0 auto; 254 | width: 150px; 255 | height: 100px; 256 | margin-bottom: 0.5em; 257 | } 258 | .home-logo svg { 259 | width: 150px; 260 | height: 100px; 261 | } 262 | .home-buttons { 263 | margin-top: 1.5em; 264 | } 265 | .home-buttons .menu { 266 | display: inline-block; 267 | padding: 0.5em 0.75em; 268 | } 269 | @media only screen and (max-width: 420px) { 270 | .home-buttons { 271 | padding-top: 0.5rem; 272 | max-width: 15em; 273 | margin: 0 auto; 274 | } 275 | .home-buttons .button { 276 | text-align: center; 277 | display: block; 278 | } 279 | .home-buttons .menu { 280 | margin-top: 0.5rem; 281 | text-align: center; 282 | display: block; 283 | } 284 | .home-buttons .dark-mode-form { 285 | float: none; 286 | margin-top: 0.5rem; 287 | } 288 | } 289 | .menu { 290 | margin-bottom: 1.5rem; 291 | } 292 | .modal { 293 | position: fixed; 294 | top: 0; 295 | right: 0; 296 | bottom: 0; 297 | left: 0; 298 | z-index: 50; 299 | overflow: auto; 300 | background-color: var(--drop-shadow); 301 | } 302 | .dark-mode .modal { 303 | background-color: var(--dark-drop-shadow); 304 | } 305 | .modal-content { 306 | display: block; 307 | position: relative; 308 | padding: 1em; 309 | background-color: var(--modal-background); 310 | max-width: 20em; 311 | height: 100%; 312 | animation-name: animateleft; 313 | animation-duration: .4s; 314 | overflow: auto; 315 | box-shadow: 0 0 10px 0 var(--drop-shadow); 316 | box-sizing: border-box; 317 | } 318 | .dark-mode .modal-content { 319 | background-color: var(--dark-modal-background); 320 | box-shadow: 0 0 10px 0 var(--dark-drop-shadow); 321 | } 322 | .modal-content .table-of-contents { 323 | padding: 2em; 324 | } 325 | .section-title { 326 | margin-bottom: 0; 327 | } 328 | .closed { 329 | display: none; 330 | } 331 | @keyframes animateleft { 332 | from {left: -300px; opacity: 0} 333 | to {left: 0; opacity: 1} 334 | } 335 | .logo-404 { 336 | max-width: 200px; 337 | max-height: 200px; 338 | margin: 0 auto; 339 | display: block; 340 | } 341 | .logo-404 svg { 342 | max-width: 200px; 343 | max-height: 200px; 344 | } 345 | .toc-404 { 346 | max-width: 200px; 347 | margin: 0 auto; 348 | } 349 | .message-404 { 350 | font-size: 2em; 351 | margin: 1em auto; 352 | text-align: center; 353 | } 354 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | baseURL = "https://phpapprentice.com/" 2 | languageCode = "en-us" 3 | title = "PHP Apprentice" 4 | uglyurls = true 5 | 6 | [markup] 7 | [markup.highlight] 8 | codeFences = false 9 | -------------------------------------------------------------------------------- /content/01-basics.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Basics" 3 | description = "Getting Started" 4 | tags = ["php", "basics"] 5 | slug = "basics" 6 | next = "variables.html" 7 | +++ 8 | If you want to follow along by writing code, start by downloading a code editor. I recommend 9 | [Visual Studio Code](https://code.visualstudio.com/) or [Sublime Text](https://www.sublimetext.com/). 10 | Next, create a new file in your editor called `basics.php` and save it anywhere on your computer, like a folder 11 | in your documents called `phpapprentice`. Now, we can write some PHP. 12 | 13 | All PHP files must start with a ` $two; // returns false 38 | $one < $two; // returns true 39 | ``` 40 | 41 | If you combine a greater than or less than symbol with an equal, 42 | it will check if the value is greater or less than or equal to another value. 43 | ```php 44 | $one <= $two; 45 | $one >= $two; 46 | ``` 47 | You can also check that two values are equal and of the same type 48 | by using three equal signs. 49 | 50 | The following comparisons return true. 51 | ```php 52 | 1 == 1; 53 | 1 == '1'; 54 | 1 == true; 55 | 1 == 1.0; 56 | 1 === 1; 57 | ``` 58 | 59 | These return false. 60 | ```php 61 | 1 === '1'; 62 | 1 === true; 63 | 1 === 1.0; 64 | ``` 65 | -------------------------------------------------------------------------------- /content/06-boolean-logic.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Boolean Logic" 3 | description = "Is it a yes or a no?" 4 | tags = ["php", "booleans"] 5 | slug = "boolean-logic" 6 | previous = "comparisons.html" 7 | next = "conditionals.html" 8 | +++ 9 | Boolean logic is used to combine booleans to return another boolean. 10 | 11 | Using double ampersands tells PHP to check if both values are true. 12 | If so, it will return true. If not, it will return false. 13 | ```php 14 | 0) { 17 | echo "While loop $num\n"; 18 | --$num; 19 | } 20 | ``` 21 | 22 | A `do while` loop is similar to a `while` loop except it always runs at least 23 | one iteration. In a classic `while` loop, no iterations may be executed if 24 | the value in parentheses is false. In a `do while`, the boolean check 25 | is not done until after the execution of an iteration. 26 | ```php 27 | $num = 0; 28 | do { 29 | echo "Do while $num\n"; 30 | ++$num; 31 | } while ($num < 5); 32 | ``` 33 | 34 | `for` loops allow you to create a more concise while loop. 35 | Inside the parentheses, the left section creates a variable before the loop 36 | starts, the middle section is the check that is done at the beginning of each loop 37 | and the third section is executed after each loop. 38 | ```php 39 | for ($i = 0; $i < 10; $i++) { 40 | echo "For loop $i\n"; 41 | } 42 | ``` 43 | 44 | A `foreach` loop allows you to easily loop over an array. 45 | An array is a list of data stored together. 46 | The `as` keyword lets you assign a variable to the value 47 | in the array for the current iteration of the loop. 48 | ```php 49 | $set = [1, 2, 3, 4, 5]; 50 | foreach ($set as $num) { 51 | echo "Array value $num\n"; 52 | } 53 | ``` 54 | 55 | In loops, you can use the keyword `break` to stop the loop execution 56 | no matter how many more iterations should run. 57 | ```php 58 | $values = ['one', 'two', 'three']; 59 | foreach ($values as $value) { 60 | if ($value === 'two') { 61 | break; 62 | } 63 | echo "Break $value\n"; 64 | } 65 | ``` 66 | 67 | The `continue` keyword stops executing the current loop iteration, 68 | but then allows the loop to continue with other iterations. 69 | ```php 70 | $values = ['one', 'skip', 'three']; 71 | foreach ($values as $value) { 72 | if ($value === 'skip') { 73 | continue; 74 | } 75 | echo "Continue $value\n"; 76 | } 77 | ``` 78 | -------------------------------------------------------------------------------- /content/09-arrays.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Arrays" 3 | description = "Time to make a list" 4 | tags = ["php", "array", "list"] 5 | slug = "arrays" 6 | previous = "loops.html" 7 | next = "functions.html" 8 | +++ 9 | In PHP, arrays are used to store a list of items in a single variable. 10 | There are two ways to create an array. 11 | 12 | First, you can use the `array` construct to pass in values separated by commas 13 | and it will return an array. 14 | ```php 15 | 'Toyota', 'model' => 'Camry']; 40 | ``` 41 | 42 | To access the value in an associative array, just use the string key in brackets 43 | after the variable name. 44 | ```php 45 | echo $car['model'] . "\n"; 46 | ``` 47 | -------------------------------------------------------------------------------- /content/10-functions.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Functions" 3 | description = "Reusable code" 4 | tags = ["php", "function"] 5 | slug = "functions" 6 | previous = "arrays.html" 7 | next = "classes.html" 8 | +++ 9 | A function allows you to store code under a name and then execute 10 | that code later. 11 | 12 | A function always starts with the 13 | function keyword followed by the name with parentheses and then 14 | opening and closing curly braces around the code. 15 | ```php 16 | `. 37 | ```php 38 | $bike = new Bicycle(); 39 | $bike->color = 'Blue'; 40 | echo $bike->color . "\n"; 41 | ``` 42 | 43 | An instance of a class is called an object. Congratulations! 44 | You are now performing object-oriented development. 45 | ```php 46 | $redBike = new Bicycle(); 47 | $redBike->color = 'Red'; 48 | echo $redBike->color . " Bike Object\n"; 49 | ``` 50 | 51 | A method is a function attached to the class. You can add a method 52 | to a class by using the `public` keyword followed by the function. A method 53 | can access the attributes and methods of an object instance using the `$this` variable. 54 | ```php 55 | class Tricycle 56 | { 57 | public $color; 58 | 59 | public function echoColor() 60 | { 61 | echo $this->color . "\n"; 62 | } 63 | } 64 | ``` 65 | 66 | You can execute a method on an object using the same `->` arrow characters with parentheses after the method name. 67 | ```php 68 | $bike = new Tricycle(); 69 | $bike->color = 'Red'; 70 | $bike->echoColor(); 71 | ``` 72 | -------------------------------------------------------------------------------- /content/12-classes-inheritance.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Classes: Inheritance" 3 | description = "Extend your objects" 4 | tags = ["php", "extend", "inheritance"] 5 | slug = "classes-inheritance" 6 | previous = "classes.html" 7 | next = "classes-visibility.html" 8 | +++ 9 | In PHP, a class can extend another class, inheriting the parent class' 10 | properties and methods. To make a class a child of another, use the `extends` 11 | keyword after the class name. 12 | ```php 13 | drive(); 30 | ``` 31 | 32 | Even though the child class inherits a parent class' properties and methods, 33 | the child can still override the parent. 34 | ```php 35 | class Tractor extends Vehicle 36 | { 37 | public function drive() 38 | { 39 | echo "driving slowly...\n"; 40 | } 41 | } 42 | ``` 43 | 44 | The drive function now outputs "driving slowly..." instead of "driving...". 45 | ```php 46 | $tractor = new Tractor(); 47 | $tractor->drive(); 48 | ``` 49 | 50 | A class can use a parent's property or method from the `$this` variable. 51 | ```php 52 | class Motorcycle extends Vehicle 53 | { 54 | public function pushPedal() 55 | { 56 | $this->drive(); 57 | } 58 | } 59 | ``` 60 | 61 | The `pushPedal` method outputs "driving...". 62 | ```php 63 | $cycle = new Motorcycle(); 64 | $cycle->pushPedal(); 65 | ``` 66 | 67 | If you override a parent's property or method, the `$this` variable will refer to the child's 68 | implementation of the property or method. To call the parent's property or method explicity, 69 | use the `parent` keyword. 70 | ```php 71 | class Racecar extends Vehicle 72 | { 73 | public function drive() 74 | { 75 | parent::drive(); 76 | 77 | echo "driving even faster...\n"; 78 | } 79 | } 80 | ``` 81 | 82 | The `drive` method on `Racecar` now outputs "driving..." and "driving even faster...". 83 | ```php 84 | $racecar = new Racecar(); 85 | $racecar->drive(); 86 | ``` 87 | -------------------------------------------------------------------------------- /content/13-classes-visibility.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Classes: Visibility" 3 | description = "Privatizing your objects" 4 | tags = ["php", "private", "protected", "visibility"] 5 | slug = "classes-visibility" 6 | previous = "classes-inheritance.html" 7 | next = "classes-constructor.html" 8 | +++ 9 | In the last chapter, we defined properties and methods on the class using the public keyword. 10 | You can also define them using the `protected` and `private` keywords. 11 | Both keywords prevent the properties and functions from being accessible outside the object. 12 | Only the object itself can use each. 13 | ```php 14 | number = $number; 23 | } 24 | } 25 | ``` 26 | 27 | We cannot set the number using `$phone->number = '123-456-7890'`. 28 | Instead, we can use the public method. 29 | ```php 30 | $phone = new Phone(); 31 | $phone->setNumber('123-456-7890'); 32 | ``` 33 | 34 | Making an attribute or function private, gives you more control over the data in the object. 35 | For example, we could prevent a number being set if it starts with a 7. 36 | ```php 37 | class Phone2 38 | { 39 | private $number; 40 | 41 | public function setNumber($number) 42 | { 43 | if (substr($number, 0, 1) !== '7') { 44 | $this->number = $number; 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | The `protected` and `private` keywords work a little differently. 51 | They both prevent functions and properties from being accessed outside an object. 52 | However, a method or property marked `protected` can still be accessed by a child class. 53 | ```php 54 | class Phone3 55 | { 56 | private $number; 57 | 58 | protected $caller; 59 | 60 | public function setNumber($number) 61 | { 62 | $this->number = $number; 63 | } 64 | } 65 | ``` 66 | 67 | In class `Smartphone`, the `caller` property is accessible because the parent class 68 | has it marked as `protected`. However, `Smartphone` cannot access the `number` property 69 | because it is still listed as private. 70 | ```php 71 | class Smartphone extends Phone3 72 | { 73 | public function setCaller($caller) 74 | { 75 | $this->caller = $caller; 76 | } 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /content/14-classes-constructor.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Classes: Constructor" 3 | description = "Construct your objects" 4 | tags = ["php", "construct", "constructor", "new"] 5 | slug = "classes-constructor" 6 | previous = "classes-visibility.html" 7 | next = "static.html" 8 | +++ 9 | Whenever you create an object in PHP, you put parentheses after the class name. 10 | In the previous examples, we always left the parentheses empty. 11 | ```php 12 | color = $color; 21 | } 22 | } 23 | 24 | $hat = new Hat(); 25 | ``` 26 | 27 | However, you can actually pass data into the parentheses like a function. 28 | The data will be passed to a special function on the class called a constructor. 29 | ```php 30 | class Ballcap 31 | { 32 | public $color; 33 | 34 | public function __construct($color) 35 | { 36 | $this->color = $color; 37 | } 38 | } 39 | ``` 40 | 41 | A constructor is not required, but can make creating a new object easier. 42 | They are usually used to define the initial value of a property. 43 | Instead of writing: 44 | ```php 45 | $hat = new Hat(); 46 | $hat->setColor('Red'); 47 | ``` 48 | 49 | You can write: 50 | ```php 51 | $ballcap = new Ballcap('Blue'); 52 | ``` 53 | 54 | Constructors do not return values because the return value is always a new object. 55 | ```php 56 | class Tophat 57 | { 58 | public function __construct($color) 59 | { 60 | return $color; 61 | } 62 | } 63 | ``` 64 | 65 | `$tophat` now holds an instance of `Tophat`, not the string "Grey". 66 | ```php 67 | $tophat = new Tophat('Grey'); 68 | ``` 69 | -------------------------------------------------------------------------------- /content/15-static.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Static" 3 | description = "Class properties and methods" 4 | tags = ["php", "static"] 5 | slug = "static" 6 | previous = "classes-constructor.html" 7 | next = "interfaces.html" 8 | +++ 9 | When writing a class, all of the properties and methods are being defined for the object 10 | that will be created from the class. 11 | ```php 12 | color = $color; 21 | } 22 | } 23 | ``` 24 | 25 | Like building a house, a class is a blueprint that 26 | defines what the house can do and the object is the house itself that can actually 27 | perform the actions defined in the blueprint. 28 | ```php 29 | $house = new House('Green'); 30 | ``` 31 | 32 | However, what if you want the blueprint to have properties and methods? 33 | That is when you use the `static` keyword. In this class, we will define a default color 34 | on the class itself and then use it when creating a new object. 35 | ```php 36 | class Skyscraper 37 | { 38 | private static $popularColor; 39 | public $color; 40 | 41 | public static function setDefaultColor($color) 42 | { 43 | self::$popularColor = $color; 44 | } 45 | 46 | public function __construct() 47 | { 48 | $this->color = self::$popularColor; 49 | } 50 | } 51 | ``` 52 | 53 | You can access static methods and properties using double colons on `self` inside the object 54 | or on the class name outside of the object. Static methods and properties can only access 55 | other static methods and properties. 56 | ```php 57 | Skyscraper::setDefaultColor('Grey'); 58 | $skyscraper = new Skyscraper(); 59 | echo $skyscraper->color . "\n"; 60 | ``` 61 | 62 | Often, you will see static constructors in PHP. 63 | A static constructor creates a new instance of an object. Why would do that when you can just use "new Class" to create 64 | the object? A common reason is to make the code more readable. 65 | ```php 66 | class TinyHouse 67 | { 68 | private $color; 69 | private $wheels; 70 | private $trailer; 71 | 72 | public static function build($color, $wheels, $trailer) 73 | { 74 | return new self($color, $wheels, $trailer); 75 | } 76 | 77 | public function __construct($color, $wheels, $trailer) 78 | { 79 | $this->color = $color; 80 | $this->wheels = $wheels; 81 | $this->trailer = $trailer; 82 | } 83 | } 84 | ``` 85 | 86 | Using `build` can make more sense than `new`, but it is ultimately a personal preference. 87 | ```php 88 | $house = TinyHouse::build('Blue', 4, true); 89 | ``` 90 | -------------------------------------------------------------------------------- /content/16-interfaces.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Interfaces" 3 | description = "Writing code contracts" 4 | tags = ["php", "interface"] 5 | slug = "interfaces" 6 | previous = "static.html" 7 | next = "abstract.html" 8 | +++ 9 | The word `interface` is a confusing term because it is used for so many different concepts. 10 | Most often, we use it to describe the appearance of an app and how a user interacts with it. 11 | However, in PHP, an interface is a special construct that acts as a contract for classes. 12 | An interface defines what methods a class should have. 13 | ```php 14 | color = $color; 35 | } 36 | 37 | public function setLegs($number) 38 | { 39 | $this->legs = $number; 40 | } 41 | } 42 | ``` 43 | 44 | Interfaces are helpful when you are using code created by someone else. 45 | For example, another developer may have created code that manages online payments, but they want to give you 46 | the ability to create your own payment class that works with their code. In that case, the developer 47 | creates an interface with all the required methods they need to charge a payment. The interface 48 | becomes a contract between your code and the other developer's code to work a certain way. 49 | ```php 50 | interface Payment 51 | { 52 | public function charge($amount); 53 | } 54 | 55 | class CreditCard implements Payment 56 | { 57 | public function charge($amount) 58 | { 59 | // contacts a credit card payment provider... 60 | } 61 | } 62 | ``` 63 | 64 | Since `CreditCard` implements `Payment`, a developer can check that it implements `Payment` and then use the `charge` method knowing the function exists on the class. 65 | ```php 66 | $creditCard = new CreditCard(); 67 | if ($creditCard instanceof Payment) { 68 | $creditCard->charge(25); 69 | } 70 | ``` 71 | -------------------------------------------------------------------------------- /content/17-abstract.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Abstract Classes" 3 | description = "Inheriting an interface" 4 | tags = ["php", "abstract"] 5 | slug = "abstract" 6 | previous = "interfaces.html" 7 | next = "exceptions.html" 8 | +++ 9 | Abstract classes are similar to interfaces in that they define methods that a sub-class must implement. 10 | However, an abstract class can also have normal methods. To create an abstract class, use the `abstract` 11 | keyword followed by `class` and the name of the class. 12 | ```php 13 | turnOn(); 55 | $iPhone->unlock(); 56 | 57 | $android = new Android(); 58 | $android->turnOn(); 59 | $android->unlock(); 60 | ``` 61 | 62 | You cannot create an instance of an abstract class. PHP would not know how to use the abstract methods 63 | so when you try to create an abstract instance you will get an error. 64 | ```php 65 | $cellPhone = new CellPhone(); // causes an error 66 | ``` 67 | -------------------------------------------------------------------------------- /content/18-exceptions.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Exceptions" 3 | description = "Throwing errors" 4 | tags = ["php", "exception"] 5 | slug = "exceptions" 6 | previous = "abstract.html" 7 | next = "web/http.html" 8 | +++ 9 | Sometimes things go wrong when someone uses your code. How do we handle this situation? 10 | PHP has Exceptions to define errors and the ability to `throw` them to stop code 11 | execution and tell the user of your code that something is wrong. 12 | ```php 13 | getNumber()) !== 16) { 20 | throw new Exception('Credit card is not right'); 21 | } 22 | } 23 | } 24 | ``` 25 | 26 | In this case, if someone tried to use the `Processor` class 27 | to charge a credit card number that is not 16 characters long, an 28 | `Exception` will be thrown which stops the rest of the code from running. 29 | ```php 30 | $processor = new Processor(); 31 | $processor->charge('1234'); 32 | ``` 33 | 34 | A developer who wants to prevent an exception from stopping code execution 35 | can catch the exception and use it for logging or display the error to a user. 36 | 37 | Just wrap the code that might throw an exception with the keyword `try` and braces 38 | followed by `catch`, the exception type in parentheses and more braces. 39 | ```php 40 | try { 41 | $processor->charge('1234'); 42 | } catch (Exception $e) { 43 | echo $e->getMessage() . "\n"; 44 | } 45 | ``` 46 | 47 | You can make your own custom exceptions as well. They are just classes 48 | that extend the `Exception` class. 49 | ```php 50 | class MyCustomException extends Exception {} 51 | ``` 52 | 53 | Then, you can try to catch your exception instead of the base exception. 54 | ```php 55 | try { 56 | throw new MyCustomException('I am a custom exception'); 57 | } catch (MyCustomException $e) { 58 | echo "Caught MyCustomException\n"; 59 | } 60 | ``` 61 | 62 | Since all exceptions inherit from `Exception`, catching 63 | `Exception` will catch any and all exceptions that might be thrown. 64 | ```php 65 | try { 66 | throw new MyCustomException('I inherit from Exception'); 67 | } catch (Exception $e) { 68 | echo "Still caught MyCustomException\n"; 69 | } 70 | ``` 71 | 72 | You can also create multiple catch blocks to handle different types of exceptions in 73 | one try-catch. 74 | ```php 75 | try { 76 | throw new MyCustomException('I am being thrown again'); 77 | } catch (MyCustomException $e) { 78 | echo "MyCustomException was caught\n"; 79 | } catch (Exception $e) { 80 | echo "Just in case a different exception is thrown\n"; 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /content/_index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/restoreddev/phpapprentice/dc3082d386e40f12d9b6c153740f1fa23e30cdce/content/_index.md -------------------------------------------------------------------------------- /content/credits.html: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Credits" 3 | +++ 4 |
5 |

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

10 |

11 | Sites used as a reference while writing PHP Apprentice: 12 |

16 |

17 |

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

30 |

31 |
32 |

33 | Created and managed by Andrew Davis @restoreddev. 34 |

35 |
36 | -------------------------------------------------------------------------------- /content/installing-php.html: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Installing PHP" 3 | +++ 4 |
5 |

Windows 10

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

MacOS

16 |

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

17 |
    18 |
  1. Install Homebrew.
  2. 19 |
  3. Run brew install php in Terminal.
  4. 20 |
  5. Check your version is correct by running php -v in Terminal.
  6. 21 |
22 |
23 | -------------------------------------------------------------------------------- /content/web/01-http.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "HTTP" 3 | description = "Understanding the format of the web" 4 | tags = ["web", "http"] 5 | slug = "http" 6 | previous = "exceptions.html" 7 | next = "web/http-post.html" 8 | +++ 9 | HTTP stands for Hypertext Transfer Protocol. It is a standard for how requests and responses should be formatted for 10 | 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 11 | is responding with a HTTP response. The browser then takes the response, parses it and displays it to the user. 12 | 13 | Let us look at a HTTP request for PHP Apprentice. This is the request sent to the server from Firefox: 14 | ```http 15 | GET /basics.html HTTP/2.0 16 | Host: phpapprentice.com 17 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:65.0) Gecko/20100101 Firefox/65.0 18 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 19 | Accept-Language: en-US,en;q=0.5 20 | Accept-Encoding: gzip, deflate, br 21 | Connection: keep-alive 22 | Upgrade-Insecure-Requests: 1 23 | Pragma: no-cache 24 | Cache-Control: no-cache 25 | ``` 26 | The first line of the request tells us three things. First, the method of the request. A `GET` request tells the server 27 | that the browser just wants to download whatever is stored at the link. The second value `/basics.html` determines 28 | what should be loaded from `phpapprentice.com`. Finally, the third value tells the server what type of HTTP request it is. 29 | 30 | All of the lines below the top line are key/value pairs called headers. They give the server different pieces of information 31 | about the client and the request to the server. For example, `Host` tells the server what website the browser is 32 | trying to access. 33 | 34 | In response to the client's HTTP request, the server will respond with a HTTP response. Here is the response for the above request: 35 | ```http 36 | HTTP/2.0 200 OK 37 | server: GitHub.com 38 | content-type: text/html; charset=utf-8 39 | last-modified: Sun, 13 Jan 2019 22:57:50 GMT 40 | etag: W/"5c3bc26e-15a0" 41 | access-control-allow-origin: * 42 | expires: Mon, 18 Feb 2019 02:38:31 GMT 43 | cache-control: max-age=600 44 | content-encoding: gzip 45 | x-github-request-id: 8596:461C:46C99E:5BB366:5C6A184F 46 | accept-ranges: bytes 47 | date: Mon, 18 Feb 2019 03:06:02 GMT 48 | via: 1.1 varnish 49 | age: 52 50 | x-served-by: cache-fty21342-FTY 51 | x-cache: HIT 52 | x-cache-hits: 2 53 | x-timer: S1550459163.720652,VS0,VE0 54 | vary: Accept-Encoding 55 | x-fastly-request-id: 00f0eec6f3037e428b8fc86742fe0f9965501a51 56 | content-length: 2084 57 | 58 | 59 | 60 | 61 | ... 62 | ``` 63 | 64 | The top line of the response is in two parts, similar to a request. 65 | The first part `HTTP/2.0` indicates the connection type, like the client. 66 | The second part `200 OK` is the response code. `200` indicates a successful response. 67 | There are many different response codes, the most famous being `404 Not Found`. 68 | 69 | A response will have headers as well, but they are to give the client information about the response. As you can see, 70 | the `Content-Type` is set to `text/html`, which tells the client how to render the content of the response. 71 | 72 | Notice that below the list of response headers, there is also the content of the response. In this case, it is just HTML 73 | which the browser can you use to display the page. 74 | 75 | HTTP requests and responses are the core of web communication. Next, we will look at a request type other than `GET`. 76 | -------------------------------------------------------------------------------- /content/web/02-http-post.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "HTTP Post" 3 | description = "Sending data to a server" 4 | tags = ["web", "http", "post"] 5 | slug = "http-post" 6 | previous = "web/http.html" 7 | next = "web/http-server.html" 8 | +++ 9 | HTTP uses multiple different request types for indicating actions that should be performed on the server. The most common ones 10 | you will use are: 11 | - GET -> Retrieve a resource 12 | - POST -> Create a new resource 13 | - PUT -> Replace an entire resource 14 | - PATCH -> Update attributes of a resource 15 | - DELETE -> Delete a resource 16 | 17 | We have already seen a GET request. The next most common request type is POST. A POST request is structured the same as a GET request, however, it will contain data in the request body: 18 | ```http 19 | POST /login.php HTTP/1.1 20 | Host: localhost:8080 21 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:65.0) Gecko/20100101 Firefox/65.0 22 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 23 | Accept-Language: en-US,en;q=0.5 24 | Accept-Encoding: gzip, deflate 25 | Referer: http://localhost:8080/login.php 26 | Content-Type: application/x-www-form-urlencoded 27 | Content-Length: 30 28 | Connection: keep-alive 29 | Upgrade-Insecure-Requests: 1 30 | Pragma: no-cache 31 | Cache-Control: no-cache 32 | 33 | username=php&password=password 34 | ``` 35 | The above request is an example login request created by Firefox. You can see in the first line that POST is used to declare the request type. 36 | At the bottom of the request, the contents contain the user's username and password. A server can easily parse the data and do something with it. In this case, it can authenticate the user. 37 | 38 | POST requests are used to send data to a server. A server will process the data and perform an action or store the data. 39 | 40 | We have been talking a lot about how a server can process requests and send responses. How do programmers develop a server to handle HTTP requests? With PHP! 41 | 42 | PHP was built with web server development in mind. Let us write our first PHP server script. 43 | -------------------------------------------------------------------------------- /content/web/03-http-server.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "PHP HTTP Server" 3 | description = "Handling HTTP Requests in PHP" 4 | tags = ["php", "http", "server"] 5 | slug = "http-server" 6 | previous = "web/http-post.html" 7 | next ="web/php-html.html" 8 | +++ 9 | 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. 10 | 11 | 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: 12 | ```php 13 | "; 33 | foreach ($headers as $key => $header) { 34 | echo "$key: $header
"; 35 | } 36 | ``` 37 | Reload `localhost:8080` and you will see the main request line and all of the headers printed on the web page. The `
` tag is HTML for a new line. Since we are outputting to a web browser, we can no longer use `"\n"` to create a new line. 38 | 39 | By default, PHP will generate the necessary response line and headers. If you open your browser console and open the network tab, you can see the response code and headers. 40 | ```http 41 | HTTP/1.1 200 OK 42 | Host: localhost:8080 43 | Date: Sun, 28 Apr 2019 10:19:25 -0500 44 | Connection: close 45 | X-Powered-By: PHP/7.2.17 46 | Content-type: text/html; charset=UTF-8 47 | ``` 48 | PHP set the response code to `200 OK` and the content type to `text/html`. Even though PHP will output good defaults, we can change them in our code if we want. Update your `index.php` file with this code: 49 | ```php 50 | 15 | 16 | 17 | PHP Apprentice - HTML 18 | 19 | 20 |

I can write HTML!

21 | 22 | 23 | ``` 24 | 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. 25 | 26 | How does the PHP runtime know when you are writing HTML or writing PHP code? It all depends on PHP tags. When you write `` as the closing tag. In PHP only files, the closing tag is unnecessary since the entire file is PHP. 27 | 28 | 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. 29 | ```php 30 | 31 | 32 | 33 | PHP Apprentice - HTML 34 | 35 | 36 |

37 | 38 | 39 | ``` 40 | 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: ``. 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. 41 | 42 | I used the PHP keyword `echo` in the last example to output the string result of `strtoupper` inside the `

` tags. PHP supports a shorthand opening tag that combines the open tag with echo: `

45 | ``` 46 | This example and the previous one are equivalent to the PHP runtime. -------------------------------------------------------------------------------- /layouts/404.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 | {{ readFile "static/elephant.svg" | safeHTML }} 4 |

Whoops! The page could not be found.

5 |
6 | {{ partial "table_of_contents.html" . }} 7 |
8 |
9 | {{ end }} 10 | -------------------------------------------------------------------------------- /layouts/_default/baseof.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ if .Title }}{{ .Title }}{{ else }}{{ .Site.Title }}{{ end }} 8 | 9 | 10 | 11 | 12 | {{ $prismSolarized := resources.Get "prism-solarized.css" }} 13 | {{ $prismTomorrowNight := resources.Get "prism-tomorrow-night.css" }} 14 | {{ $securePrismSolarized := $prismSolarized | resources.Fingerprint "sha512" }} 15 | {{ $securePrismTomorrowNight := $prismTomorrowNight | resources.Fingerprint "sha512" }} 16 | 17 | {{ $css := resources.Get "styles.css" }} 18 | {{ $style := $css | resources.PostCSS }} 19 | {{ $secureCss := $style | resources.Fingerprint "sha512" }} 20 | 21 | 22 | 23 | 24 | 25 | 26 | {{ $darkMode := resources.Get "dark-mode.js" }} 27 | {{ $darkModeSecure := $darkMode | resources.Fingerprint "sha512" }} 28 | 29 | 30 | {{ block "main" . }}{{ end }} 31 | 32 | {{ $prism := resources.Get "prism.js" }} 33 | {{ $site := resources.Get "site.js"}} 34 | {{ $prismSecure := $prism | resources.Fingerprint "sha512" }} 35 | {{ $siteSecure := $site | resources.Fingerprint "sha512" }} 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /layouts/_default/index.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 |
4 |
5 | 6 |

PHP Apprentice

7 |

An online book for learning PHP

8 |
9 |

10 | 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. 11 |

12 |

13 | 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. 14 |

15 |

16 | 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. 17 |

18 |

19 | PHP Apprentice is currently a work in progress. If you would like to give feedback or request a certain discussion topic, check out the GitHub repository. 20 |

21 | 28 |
29 |

Created and managed by Andrew Davis @restoreddev 30 |

31 |
32 | 33 | {{ partial "menu_modal.html" . }} 34 | {{ end }} 35 | -------------------------------------------------------------------------------- /layouts/_default/list.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/restoreddev/phpapprentice/dc3082d386e40f12d9b6c153740f1fa23e30cdce/layouts/_default/list.html -------------------------------------------------------------------------------- /layouts/_default/single.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 | {{ partial "menu_button.html" . }} 4 | 5 |

{{ .Title }}

6 |

{{ .Description }}

7 | {{ .Content }} 8 | 9 |
10 | 24 |
25 | {{ partial "menu_modal.html" . }} 26 | {{ end }} 27 | -------------------------------------------------------------------------------- /layouts/partials/menu_button.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /layouts/partials/menu_modal.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /layouts/partials/table_of_contents.html: -------------------------------------------------------------------------------- 1 |
2 |

Table of Contents

3 | 7 | 8 |
Basics
9 |
    10 |
  1. Basics
  2. 11 |
  3. Variables
  4. 12 |
  5. Arithmetic
  6. 13 |
  7. Strings
  8. 14 |
  9. Comparisons
  10. 15 |
  11. Boolean Logic
  12. 16 |
  13. Conditionals
  14. 17 |
  15. Loops
  16. 18 |
  17. Arrays
  18. 19 |
  19. Functions
  20. 20 |
  21. Classes
  22. 21 |
  23. Classes: Inheritance
  24. 22 |
  25. Classes: Visibility
  26. 23 |
  27. Classes: Constructor
  28. 24 |
  29. Static
  30. 25 |
  31. Interfaces
  32. 26 |
  33. Abstract Classes
  34. 27 |
  35. Exceptions
  36. 28 |
29 | 30 |
Web
31 |
    32 |
  1. HTTP
  2. 33 |
  3. HTTP POST
  4. 34 |
  5. PHP HTTP Server
  6. 35 |
  7. PHP HTML
  8. 36 |
37 | 38 | Credits 39 |
40 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phpapprentice", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "phpapprentice", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "autoprefixer": "^10.4.8", 13 | "postcss-cli": "^10.0.0", 14 | "postcss-custom-properties": "^12.0.0" 15 | } 16 | }, 17 | "node_modules/@nodelib/fs.scandir": { 18 | "version": "2.1.5", 19 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 20 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 21 | "dev": true, 22 | "dependencies": { 23 | "@nodelib/fs.stat": "2.0.5", 24 | "run-parallel": "^1.1.9" 25 | }, 26 | "engines": { 27 | "node": ">= 8" 28 | } 29 | }, 30 | "node_modules/@nodelib/fs.stat": { 31 | "version": "2.0.5", 32 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 33 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 34 | "dev": true, 35 | "engines": { 36 | "node": ">= 8" 37 | } 38 | }, 39 | "node_modules/@nodelib/fs.walk": { 40 | "version": "1.2.8", 41 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 42 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 43 | "dev": true, 44 | "dependencies": { 45 | "@nodelib/fs.scandir": "2.1.5", 46 | "fastq": "^1.6.0" 47 | }, 48 | "engines": { 49 | "node": ">= 8" 50 | } 51 | }, 52 | "node_modules/ansi-regex": { 53 | "version": "5.0.1", 54 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 55 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 56 | "dev": true, 57 | "engines": { 58 | "node": ">=8" 59 | } 60 | }, 61 | "node_modules/ansi-styles": { 62 | "version": "4.3.0", 63 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 64 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 65 | "dev": true, 66 | "dependencies": { 67 | "color-convert": "^2.0.1" 68 | }, 69 | "engines": { 70 | "node": ">=8" 71 | }, 72 | "funding": { 73 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 74 | } 75 | }, 76 | "node_modules/anymatch": { 77 | "version": "3.1.2", 78 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 79 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 80 | "dev": true, 81 | "dependencies": { 82 | "normalize-path": "^3.0.0", 83 | "picomatch": "^2.0.4" 84 | }, 85 | "engines": { 86 | "node": ">= 8" 87 | } 88 | }, 89 | "node_modules/autoprefixer": { 90 | "version": "10.4.8", 91 | "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz", 92 | "integrity": "sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==", 93 | "dev": true, 94 | "funding": [ 95 | { 96 | "type": "opencollective", 97 | "url": "https://opencollective.com/postcss/" 98 | }, 99 | { 100 | "type": "tidelift", 101 | "url": "https://tidelift.com/funding/github/npm/autoprefixer" 102 | } 103 | ], 104 | "dependencies": { 105 | "browserslist": "^4.21.3", 106 | "caniuse-lite": "^1.0.30001373", 107 | "fraction.js": "^4.2.0", 108 | "normalize-range": "^0.1.2", 109 | "picocolors": "^1.0.0", 110 | "postcss-value-parser": "^4.2.0" 111 | }, 112 | "bin": { 113 | "autoprefixer": "bin/autoprefixer" 114 | }, 115 | "engines": { 116 | "node": "^10 || ^12 || >=14" 117 | }, 118 | "peerDependencies": { 119 | "postcss": "^8.1.0" 120 | } 121 | }, 122 | "node_modules/binary-extensions": { 123 | "version": "2.2.0", 124 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 125 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 126 | "dev": true, 127 | "engines": { 128 | "node": ">=8" 129 | } 130 | }, 131 | "node_modules/braces": { 132 | "version": "3.0.2", 133 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 134 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 135 | "dev": true, 136 | "dependencies": { 137 | "fill-range": "^7.0.1" 138 | }, 139 | "engines": { 140 | "node": ">=8" 141 | } 142 | }, 143 | "node_modules/browserslist": { 144 | "version": "4.21.3", 145 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", 146 | "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", 147 | "dev": true, 148 | "funding": [ 149 | { 150 | "type": "opencollective", 151 | "url": "https://opencollective.com/browserslist" 152 | }, 153 | { 154 | "type": "tidelift", 155 | "url": "https://tidelift.com/funding/github/npm/browserslist" 156 | } 157 | ], 158 | "dependencies": { 159 | "caniuse-lite": "^1.0.30001370", 160 | "electron-to-chromium": "^1.4.202", 161 | "node-releases": "^2.0.6", 162 | "update-browserslist-db": "^1.0.5" 163 | }, 164 | "bin": { 165 | "browserslist": "cli.js" 166 | }, 167 | "engines": { 168 | "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 169 | } 170 | }, 171 | "node_modules/caniuse-lite": { 172 | "version": "1.0.30001393", 173 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001393.tgz", 174 | "integrity": "sha512-N/od11RX+Gsk+1qY/jbPa0R6zJupEa0lxeBG598EbrtblxVCTJsQwbRBm6+V+rxpc5lHKdsXb9RY83cZIPLseA==", 175 | "dev": true, 176 | "funding": [ 177 | { 178 | "type": "opencollective", 179 | "url": "https://opencollective.com/browserslist" 180 | }, 181 | { 182 | "type": "tidelift", 183 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 184 | } 185 | ] 186 | }, 187 | "node_modules/chokidar": { 188 | "version": "3.5.3", 189 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 190 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 191 | "dev": true, 192 | "funding": [ 193 | { 194 | "type": "individual", 195 | "url": "https://paulmillr.com/funding/" 196 | } 197 | ], 198 | "dependencies": { 199 | "anymatch": "~3.1.2", 200 | "braces": "~3.0.2", 201 | "glob-parent": "~5.1.2", 202 | "is-binary-path": "~2.1.0", 203 | "is-glob": "~4.0.1", 204 | "normalize-path": "~3.0.0", 205 | "readdirp": "~3.6.0" 206 | }, 207 | "engines": { 208 | "node": ">= 8.10.0" 209 | }, 210 | "optionalDependencies": { 211 | "fsevents": "~2.3.2" 212 | } 213 | }, 214 | "node_modules/cliui": { 215 | "version": "7.0.4", 216 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 217 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 218 | "dev": true, 219 | "dependencies": { 220 | "string-width": "^4.2.0", 221 | "strip-ansi": "^6.0.0", 222 | "wrap-ansi": "^7.0.0" 223 | } 224 | }, 225 | "node_modules/color-convert": { 226 | "version": "2.0.1", 227 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 228 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 229 | "dev": true, 230 | "dependencies": { 231 | "color-name": "~1.1.4" 232 | }, 233 | "engines": { 234 | "node": ">=7.0.0" 235 | } 236 | }, 237 | "node_modules/color-name": { 238 | "version": "1.1.4", 239 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 240 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 241 | "dev": true 242 | }, 243 | "node_modules/dependency-graph": { 244 | "version": "0.11.0", 245 | "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", 246 | "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", 247 | "dev": true, 248 | "engines": { 249 | "node": ">= 0.6.0" 250 | } 251 | }, 252 | "node_modules/dir-glob": { 253 | "version": "3.0.1", 254 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 255 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 256 | "dev": true, 257 | "dependencies": { 258 | "path-type": "^4.0.0" 259 | }, 260 | "engines": { 261 | "node": ">=8" 262 | } 263 | }, 264 | "node_modules/electron-to-chromium": { 265 | "version": "1.4.247", 266 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.247.tgz", 267 | "integrity": "sha512-FLs6R4FQE+1JHM0hh3sfdxnYjKvJpHZyhQDjc2qFq/xFvmmRt/TATNToZhrcGUFzpF2XjeiuozrA8lI0PZmYYw==", 268 | "dev": true 269 | }, 270 | "node_modules/emoji-regex": { 271 | "version": "8.0.0", 272 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 273 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 274 | "dev": true 275 | }, 276 | "node_modules/escalade": { 277 | "version": "3.1.1", 278 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 279 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 280 | "dev": true, 281 | "engines": { 282 | "node": ">=6" 283 | } 284 | }, 285 | "node_modules/fast-glob": { 286 | "version": "3.2.12", 287 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", 288 | "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", 289 | "dev": true, 290 | "dependencies": { 291 | "@nodelib/fs.stat": "^2.0.2", 292 | "@nodelib/fs.walk": "^1.2.3", 293 | "glob-parent": "^5.1.2", 294 | "merge2": "^1.3.0", 295 | "micromatch": "^4.0.4" 296 | }, 297 | "engines": { 298 | "node": ">=8.6.0" 299 | } 300 | }, 301 | "node_modules/fastq": { 302 | "version": "1.13.0", 303 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", 304 | "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", 305 | "dev": true, 306 | "dependencies": { 307 | "reusify": "^1.0.4" 308 | } 309 | }, 310 | "node_modules/fill-range": { 311 | "version": "7.0.1", 312 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 313 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 314 | "dev": true, 315 | "dependencies": { 316 | "to-regex-range": "^5.0.1" 317 | }, 318 | "engines": { 319 | "node": ">=8" 320 | } 321 | }, 322 | "node_modules/fraction.js": { 323 | "version": "4.2.0", 324 | "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", 325 | "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", 326 | "dev": true, 327 | "engines": { 328 | "node": "*" 329 | }, 330 | "funding": { 331 | "type": "patreon", 332 | "url": "https://www.patreon.com/infusion" 333 | } 334 | }, 335 | "node_modules/fs-extra": { 336 | "version": "10.1.0", 337 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", 338 | "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", 339 | "dev": true, 340 | "dependencies": { 341 | "graceful-fs": "^4.2.0", 342 | "jsonfile": "^6.0.1", 343 | "universalify": "^2.0.0" 344 | }, 345 | "engines": { 346 | "node": ">=12" 347 | } 348 | }, 349 | "node_modules/fsevents": { 350 | "version": "2.3.2", 351 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 352 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 353 | "dev": true, 354 | "hasInstallScript": true, 355 | "optional": true, 356 | "os": [ 357 | "darwin" 358 | ], 359 | "engines": { 360 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 361 | } 362 | }, 363 | "node_modules/get-caller-file": { 364 | "version": "2.0.5", 365 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 366 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 367 | "dev": true, 368 | "engines": { 369 | "node": "6.* || 8.* || >= 10.*" 370 | } 371 | }, 372 | "node_modules/get-stdin": { 373 | "version": "9.0.0", 374 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", 375 | "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", 376 | "dev": true, 377 | "engines": { 378 | "node": ">=12" 379 | }, 380 | "funding": { 381 | "url": "https://github.com/sponsors/sindresorhus" 382 | } 383 | }, 384 | "node_modules/glob-parent": { 385 | "version": "5.1.2", 386 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 387 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 388 | "dev": true, 389 | "dependencies": { 390 | "is-glob": "^4.0.1" 391 | }, 392 | "engines": { 393 | "node": ">= 6" 394 | } 395 | }, 396 | "node_modules/globby": { 397 | "version": "13.1.2", 398 | "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", 399 | "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", 400 | "dev": true, 401 | "dependencies": { 402 | "dir-glob": "^3.0.1", 403 | "fast-glob": "^3.2.11", 404 | "ignore": "^5.2.0", 405 | "merge2": "^1.4.1", 406 | "slash": "^4.0.0" 407 | }, 408 | "engines": { 409 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 410 | }, 411 | "funding": { 412 | "url": "https://github.com/sponsors/sindresorhus" 413 | } 414 | }, 415 | "node_modules/graceful-fs": { 416 | "version": "4.2.10", 417 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", 418 | "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", 419 | "dev": true 420 | }, 421 | "node_modules/ignore": { 422 | "version": "5.2.0", 423 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 424 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", 425 | "dev": true, 426 | "engines": { 427 | "node": ">= 4" 428 | } 429 | }, 430 | "node_modules/is-binary-path": { 431 | "version": "2.1.0", 432 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 433 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 434 | "dev": true, 435 | "dependencies": { 436 | "binary-extensions": "^2.0.0" 437 | }, 438 | "engines": { 439 | "node": ">=8" 440 | } 441 | }, 442 | "node_modules/is-extglob": { 443 | "version": "2.1.1", 444 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 445 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 446 | "dev": true, 447 | "engines": { 448 | "node": ">=0.10.0" 449 | } 450 | }, 451 | "node_modules/is-fullwidth-code-point": { 452 | "version": "3.0.0", 453 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 454 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 455 | "dev": true, 456 | "engines": { 457 | "node": ">=8" 458 | } 459 | }, 460 | "node_modules/is-glob": { 461 | "version": "4.0.3", 462 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 463 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 464 | "dev": true, 465 | "dependencies": { 466 | "is-extglob": "^2.1.1" 467 | }, 468 | "engines": { 469 | "node": ">=0.10.0" 470 | } 471 | }, 472 | "node_modules/is-number": { 473 | "version": "7.0.0", 474 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 475 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 476 | "dev": true, 477 | "engines": { 478 | "node": ">=0.12.0" 479 | } 480 | }, 481 | "node_modules/jsonfile": { 482 | "version": "6.1.0", 483 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", 484 | "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", 485 | "dev": true, 486 | "dependencies": { 487 | "universalify": "^2.0.0" 488 | }, 489 | "optionalDependencies": { 490 | "graceful-fs": "^4.1.6" 491 | } 492 | }, 493 | "node_modules/lilconfig": { 494 | "version": "2.0.6", 495 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", 496 | "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", 497 | "dev": true, 498 | "engines": { 499 | "node": ">=10" 500 | } 501 | }, 502 | "node_modules/merge2": { 503 | "version": "1.4.1", 504 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 505 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 506 | "dev": true, 507 | "engines": { 508 | "node": ">= 8" 509 | } 510 | }, 511 | "node_modules/micromatch": { 512 | "version": "4.0.5", 513 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 514 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 515 | "dev": true, 516 | "dependencies": { 517 | "braces": "^3.0.2", 518 | "picomatch": "^2.3.1" 519 | }, 520 | "engines": { 521 | "node": ">=8.6" 522 | } 523 | }, 524 | "node_modules/nanoid": { 525 | "version": "3.3.4", 526 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 527 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", 528 | "dev": true, 529 | "peer": true, 530 | "bin": { 531 | "nanoid": "bin/nanoid.cjs" 532 | }, 533 | "engines": { 534 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 535 | } 536 | }, 537 | "node_modules/node-releases": { 538 | "version": "2.0.6", 539 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", 540 | "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", 541 | "dev": true 542 | }, 543 | "node_modules/normalize-path": { 544 | "version": "3.0.0", 545 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 546 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 547 | "dev": true, 548 | "engines": { 549 | "node": ">=0.10.0" 550 | } 551 | }, 552 | "node_modules/normalize-range": { 553 | "version": "0.1.2", 554 | "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", 555 | "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", 556 | "dev": true, 557 | "engines": { 558 | "node": ">=0.10.0" 559 | } 560 | }, 561 | "node_modules/path-type": { 562 | "version": "4.0.0", 563 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 564 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 565 | "dev": true, 566 | "engines": { 567 | "node": ">=8" 568 | } 569 | }, 570 | "node_modules/picocolors": { 571 | "version": "1.0.0", 572 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 573 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 574 | "dev": true 575 | }, 576 | "node_modules/picomatch": { 577 | "version": "2.3.1", 578 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 579 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 580 | "dev": true, 581 | "engines": { 582 | "node": ">=8.6" 583 | }, 584 | "funding": { 585 | "url": "https://github.com/sponsors/jonschlinkert" 586 | } 587 | }, 588 | "node_modules/pify": { 589 | "version": "2.3.0", 590 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 591 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 592 | "dev": true, 593 | "engines": { 594 | "node": ">=0.10.0" 595 | } 596 | }, 597 | "node_modules/postcss": { 598 | "version": "8.4.16", 599 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", 600 | "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", 601 | "dev": true, 602 | "funding": [ 603 | { 604 | "type": "opencollective", 605 | "url": "https://opencollective.com/postcss/" 606 | }, 607 | { 608 | "type": "tidelift", 609 | "url": "https://tidelift.com/funding/github/npm/postcss" 610 | } 611 | ], 612 | "peer": true, 613 | "dependencies": { 614 | "nanoid": "^3.3.4", 615 | "picocolors": "^1.0.0", 616 | "source-map-js": "^1.0.2" 617 | }, 618 | "engines": { 619 | "node": "^10 || ^12 || >=14" 620 | } 621 | }, 622 | "node_modules/postcss-cli": { 623 | "version": "10.0.0", 624 | "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-10.0.0.tgz", 625 | "integrity": "sha512-Wjy/00wBBEgQqnSToznxLWDnATznokFGXsHtF/3G8glRZpz5KYlfHcBW/VMJmWAeF2x49zjgy4izjM3/Wx1dKA==", 626 | "dev": true, 627 | "dependencies": { 628 | "chokidar": "^3.3.0", 629 | "dependency-graph": "^0.11.0", 630 | "fs-extra": "^10.0.0", 631 | "get-stdin": "^9.0.0", 632 | "globby": "^13.0.0", 633 | "picocolors": "^1.0.0", 634 | "postcss-load-config": "^4.0.0", 635 | "postcss-reporter": "^7.0.0", 636 | "pretty-hrtime": "^1.0.3", 637 | "read-cache": "^1.0.0", 638 | "slash": "^4.0.0", 639 | "yargs": "^17.0.0" 640 | }, 641 | "bin": { 642 | "postcss": "index.js" 643 | }, 644 | "engines": { 645 | "node": ">=14" 646 | }, 647 | "peerDependencies": { 648 | "postcss": "^8.0.0" 649 | } 650 | }, 651 | "node_modules/postcss-custom-properties": { 652 | "version": "12.1.8", 653 | "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", 654 | "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", 655 | "dev": true, 656 | "dependencies": { 657 | "postcss-value-parser": "^4.2.0" 658 | }, 659 | "engines": { 660 | "node": "^12 || ^14 || >=16" 661 | }, 662 | "funding": { 663 | "type": "opencollective", 664 | "url": "https://opencollective.com/csstools" 665 | }, 666 | "peerDependencies": { 667 | "postcss": "^8.4" 668 | } 669 | }, 670 | "node_modules/postcss-load-config": { 671 | "version": "4.0.1", 672 | "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", 673 | "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", 674 | "dev": true, 675 | "dependencies": { 676 | "lilconfig": "^2.0.5", 677 | "yaml": "^2.1.1" 678 | }, 679 | "engines": { 680 | "node": ">= 14" 681 | }, 682 | "funding": { 683 | "type": "opencollective", 684 | "url": "https://opencollective.com/postcss/" 685 | }, 686 | "peerDependencies": { 687 | "postcss": ">=8.0.9", 688 | "ts-node": ">=9.0.0" 689 | }, 690 | "peerDependenciesMeta": { 691 | "postcss": { 692 | "optional": true 693 | }, 694 | "ts-node": { 695 | "optional": true 696 | } 697 | } 698 | }, 699 | "node_modules/postcss-reporter": { 700 | "version": "7.0.5", 701 | "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.0.5.tgz", 702 | "integrity": "sha512-glWg7VZBilooZGOFPhN9msJ3FQs19Hie7l5a/eE6WglzYqVeH3ong3ShFcp9kDWJT1g2Y/wd59cocf9XxBtkWA==", 703 | "dev": true, 704 | "dependencies": { 705 | "picocolors": "^1.0.0", 706 | "thenby": "^1.3.4" 707 | }, 708 | "engines": { 709 | "node": ">=10" 710 | }, 711 | "funding": { 712 | "type": "opencollective", 713 | "url": "https://opencollective.com/postcss/" 714 | }, 715 | "peerDependencies": { 716 | "postcss": "^8.1.0" 717 | } 718 | }, 719 | "node_modules/postcss-value-parser": { 720 | "version": "4.2.0", 721 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 722 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 723 | "dev": true 724 | }, 725 | "node_modules/pretty-hrtime": { 726 | "version": "1.0.3", 727 | "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", 728 | "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", 729 | "dev": true, 730 | "engines": { 731 | "node": ">= 0.8" 732 | } 733 | }, 734 | "node_modules/queue-microtask": { 735 | "version": "1.2.3", 736 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 737 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 738 | "dev": true, 739 | "funding": [ 740 | { 741 | "type": "github", 742 | "url": "https://github.com/sponsors/feross" 743 | }, 744 | { 745 | "type": "patreon", 746 | "url": "https://www.patreon.com/feross" 747 | }, 748 | { 749 | "type": "consulting", 750 | "url": "https://feross.org/support" 751 | } 752 | ] 753 | }, 754 | "node_modules/read-cache": { 755 | "version": "1.0.0", 756 | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 757 | "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 758 | "dev": true, 759 | "dependencies": { 760 | "pify": "^2.3.0" 761 | } 762 | }, 763 | "node_modules/readdirp": { 764 | "version": "3.6.0", 765 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 766 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 767 | "dev": true, 768 | "dependencies": { 769 | "picomatch": "^2.2.1" 770 | }, 771 | "engines": { 772 | "node": ">=8.10.0" 773 | } 774 | }, 775 | "node_modules/require-directory": { 776 | "version": "2.1.1", 777 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 778 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 779 | "dev": true, 780 | "engines": { 781 | "node": ">=0.10.0" 782 | } 783 | }, 784 | "node_modules/reusify": { 785 | "version": "1.0.4", 786 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 787 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 788 | "dev": true, 789 | "engines": { 790 | "iojs": ">=1.0.0", 791 | "node": ">=0.10.0" 792 | } 793 | }, 794 | "node_modules/run-parallel": { 795 | "version": "1.2.0", 796 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 797 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 798 | "dev": true, 799 | "funding": [ 800 | { 801 | "type": "github", 802 | "url": "https://github.com/sponsors/feross" 803 | }, 804 | { 805 | "type": "patreon", 806 | "url": "https://www.patreon.com/feross" 807 | }, 808 | { 809 | "type": "consulting", 810 | "url": "https://feross.org/support" 811 | } 812 | ], 813 | "dependencies": { 814 | "queue-microtask": "^1.2.2" 815 | } 816 | }, 817 | "node_modules/slash": { 818 | "version": "4.0.0", 819 | "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", 820 | "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", 821 | "dev": true, 822 | "engines": { 823 | "node": ">=12" 824 | }, 825 | "funding": { 826 | "url": "https://github.com/sponsors/sindresorhus" 827 | } 828 | }, 829 | "node_modules/source-map-js": { 830 | "version": "1.0.2", 831 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 832 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 833 | "dev": true, 834 | "peer": true, 835 | "engines": { 836 | "node": ">=0.10.0" 837 | } 838 | }, 839 | "node_modules/string-width": { 840 | "version": "4.2.3", 841 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 842 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 843 | "dev": true, 844 | "dependencies": { 845 | "emoji-regex": "^8.0.0", 846 | "is-fullwidth-code-point": "^3.0.0", 847 | "strip-ansi": "^6.0.1" 848 | }, 849 | "engines": { 850 | "node": ">=8" 851 | } 852 | }, 853 | "node_modules/strip-ansi": { 854 | "version": "6.0.1", 855 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 856 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 857 | "dev": true, 858 | "dependencies": { 859 | "ansi-regex": "^5.0.1" 860 | }, 861 | "engines": { 862 | "node": ">=8" 863 | } 864 | }, 865 | "node_modules/thenby": { 866 | "version": "1.3.4", 867 | "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", 868 | "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", 869 | "dev": true 870 | }, 871 | "node_modules/to-regex-range": { 872 | "version": "5.0.1", 873 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 874 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 875 | "dev": true, 876 | "dependencies": { 877 | "is-number": "^7.0.0" 878 | }, 879 | "engines": { 880 | "node": ">=8.0" 881 | } 882 | }, 883 | "node_modules/universalify": { 884 | "version": "2.0.0", 885 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", 886 | "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", 887 | "dev": true, 888 | "engines": { 889 | "node": ">= 10.0.0" 890 | } 891 | }, 892 | "node_modules/update-browserslist-db": { 893 | "version": "1.0.7", 894 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.7.tgz", 895 | "integrity": "sha512-iN/XYesmZ2RmmWAiI4Z5rq0YqSiv0brj9Ce9CfhNE4xIW2h+MFxcgkxIzZ+ShkFPUkjU3gQ+3oypadD3RAMtrg==", 896 | "dev": true, 897 | "funding": [ 898 | { 899 | "type": "opencollective", 900 | "url": "https://opencollective.com/browserslist" 901 | }, 902 | { 903 | "type": "tidelift", 904 | "url": "https://tidelift.com/funding/github/npm/browserslist" 905 | } 906 | ], 907 | "dependencies": { 908 | "escalade": "^3.1.1", 909 | "picocolors": "^1.0.0" 910 | }, 911 | "bin": { 912 | "browserslist-lint": "cli.js" 913 | }, 914 | "peerDependencies": { 915 | "browserslist": ">= 4.21.0" 916 | } 917 | }, 918 | "node_modules/wrap-ansi": { 919 | "version": "7.0.0", 920 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 921 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 922 | "dev": true, 923 | "dependencies": { 924 | "ansi-styles": "^4.0.0", 925 | "string-width": "^4.1.0", 926 | "strip-ansi": "^6.0.0" 927 | }, 928 | "engines": { 929 | "node": ">=10" 930 | }, 931 | "funding": { 932 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 933 | } 934 | }, 935 | "node_modules/y18n": { 936 | "version": "5.0.8", 937 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 938 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 939 | "dev": true, 940 | "engines": { 941 | "node": ">=10" 942 | } 943 | }, 944 | "node_modules/yaml": { 945 | "version": "2.1.1", 946 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", 947 | "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", 948 | "dev": true, 949 | "engines": { 950 | "node": ">= 14" 951 | } 952 | }, 953 | "node_modules/yargs": { 954 | "version": "17.5.1", 955 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", 956 | "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", 957 | "dev": true, 958 | "dependencies": { 959 | "cliui": "^7.0.2", 960 | "escalade": "^3.1.1", 961 | "get-caller-file": "^2.0.5", 962 | "require-directory": "^2.1.1", 963 | "string-width": "^4.2.3", 964 | "y18n": "^5.0.5", 965 | "yargs-parser": "^21.0.0" 966 | }, 967 | "engines": { 968 | "node": ">=12" 969 | } 970 | }, 971 | "node_modules/yargs-parser": { 972 | "version": "21.1.1", 973 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 974 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 975 | "dev": true, 976 | "engines": { 977 | "node": ">=12" 978 | } 979 | } 980 | }, 981 | "dependencies": { 982 | "@nodelib/fs.scandir": { 983 | "version": "2.1.5", 984 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 985 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 986 | "dev": true, 987 | "requires": { 988 | "@nodelib/fs.stat": "2.0.5", 989 | "run-parallel": "^1.1.9" 990 | } 991 | }, 992 | "@nodelib/fs.stat": { 993 | "version": "2.0.5", 994 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 995 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 996 | "dev": true 997 | }, 998 | "@nodelib/fs.walk": { 999 | "version": "1.2.8", 1000 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 1001 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 1002 | "dev": true, 1003 | "requires": { 1004 | "@nodelib/fs.scandir": "2.1.5", 1005 | "fastq": "^1.6.0" 1006 | } 1007 | }, 1008 | "ansi-regex": { 1009 | "version": "5.0.1", 1010 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1011 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1012 | "dev": true 1013 | }, 1014 | "ansi-styles": { 1015 | "version": "4.3.0", 1016 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1017 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1018 | "dev": true, 1019 | "requires": { 1020 | "color-convert": "^2.0.1" 1021 | } 1022 | }, 1023 | "anymatch": { 1024 | "version": "3.1.2", 1025 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 1026 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 1027 | "dev": true, 1028 | "requires": { 1029 | "normalize-path": "^3.0.0", 1030 | "picomatch": "^2.0.4" 1031 | } 1032 | }, 1033 | "autoprefixer": { 1034 | "version": "10.4.8", 1035 | "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz", 1036 | "integrity": "sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==", 1037 | "dev": true, 1038 | "requires": { 1039 | "browserslist": "^4.21.3", 1040 | "caniuse-lite": "^1.0.30001373", 1041 | "fraction.js": "^4.2.0", 1042 | "normalize-range": "^0.1.2", 1043 | "picocolors": "^1.0.0", 1044 | "postcss-value-parser": "^4.2.0" 1045 | } 1046 | }, 1047 | "binary-extensions": { 1048 | "version": "2.2.0", 1049 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1050 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 1051 | "dev": true 1052 | }, 1053 | "braces": { 1054 | "version": "3.0.2", 1055 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 1056 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 1057 | "dev": true, 1058 | "requires": { 1059 | "fill-range": "^7.0.1" 1060 | } 1061 | }, 1062 | "browserslist": { 1063 | "version": "4.21.3", 1064 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", 1065 | "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", 1066 | "dev": true, 1067 | "requires": { 1068 | "caniuse-lite": "^1.0.30001370", 1069 | "electron-to-chromium": "^1.4.202", 1070 | "node-releases": "^2.0.6", 1071 | "update-browserslist-db": "^1.0.5" 1072 | } 1073 | }, 1074 | "caniuse-lite": { 1075 | "version": "1.0.30001393", 1076 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001393.tgz", 1077 | "integrity": "sha512-N/od11RX+Gsk+1qY/jbPa0R6zJupEa0lxeBG598EbrtblxVCTJsQwbRBm6+V+rxpc5lHKdsXb9RY83cZIPLseA==", 1078 | "dev": true 1079 | }, 1080 | "chokidar": { 1081 | "version": "3.5.3", 1082 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 1083 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 1084 | "dev": true, 1085 | "requires": { 1086 | "anymatch": "~3.1.2", 1087 | "braces": "~3.0.2", 1088 | "fsevents": "~2.3.2", 1089 | "glob-parent": "~5.1.2", 1090 | "is-binary-path": "~2.1.0", 1091 | "is-glob": "~4.0.1", 1092 | "normalize-path": "~3.0.0", 1093 | "readdirp": "~3.6.0" 1094 | } 1095 | }, 1096 | "cliui": { 1097 | "version": "7.0.4", 1098 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 1099 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 1100 | "dev": true, 1101 | "requires": { 1102 | "string-width": "^4.2.0", 1103 | "strip-ansi": "^6.0.0", 1104 | "wrap-ansi": "^7.0.0" 1105 | } 1106 | }, 1107 | "color-convert": { 1108 | "version": "2.0.1", 1109 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1110 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1111 | "dev": true, 1112 | "requires": { 1113 | "color-name": "~1.1.4" 1114 | } 1115 | }, 1116 | "color-name": { 1117 | "version": "1.1.4", 1118 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1119 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1120 | "dev": true 1121 | }, 1122 | "dependency-graph": { 1123 | "version": "0.11.0", 1124 | "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", 1125 | "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", 1126 | "dev": true 1127 | }, 1128 | "dir-glob": { 1129 | "version": "3.0.1", 1130 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 1131 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 1132 | "dev": true, 1133 | "requires": { 1134 | "path-type": "^4.0.0" 1135 | } 1136 | }, 1137 | "electron-to-chromium": { 1138 | "version": "1.4.247", 1139 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.247.tgz", 1140 | "integrity": "sha512-FLs6R4FQE+1JHM0hh3sfdxnYjKvJpHZyhQDjc2qFq/xFvmmRt/TATNToZhrcGUFzpF2XjeiuozrA8lI0PZmYYw==", 1141 | "dev": true 1142 | }, 1143 | "emoji-regex": { 1144 | "version": "8.0.0", 1145 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1146 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1147 | "dev": true 1148 | }, 1149 | "escalade": { 1150 | "version": "3.1.1", 1151 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1152 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1153 | "dev": true 1154 | }, 1155 | "fast-glob": { 1156 | "version": "3.2.12", 1157 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", 1158 | "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", 1159 | "dev": true, 1160 | "requires": { 1161 | "@nodelib/fs.stat": "^2.0.2", 1162 | "@nodelib/fs.walk": "^1.2.3", 1163 | "glob-parent": "^5.1.2", 1164 | "merge2": "^1.3.0", 1165 | "micromatch": "^4.0.4" 1166 | } 1167 | }, 1168 | "fastq": { 1169 | "version": "1.13.0", 1170 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", 1171 | "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", 1172 | "dev": true, 1173 | "requires": { 1174 | "reusify": "^1.0.4" 1175 | } 1176 | }, 1177 | "fill-range": { 1178 | "version": "7.0.1", 1179 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1180 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1181 | "dev": true, 1182 | "requires": { 1183 | "to-regex-range": "^5.0.1" 1184 | } 1185 | }, 1186 | "fraction.js": { 1187 | "version": "4.2.0", 1188 | "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", 1189 | "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", 1190 | "dev": true 1191 | }, 1192 | "fs-extra": { 1193 | "version": "10.1.0", 1194 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", 1195 | "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", 1196 | "dev": true, 1197 | "requires": { 1198 | "graceful-fs": "^4.2.0", 1199 | "jsonfile": "^6.0.1", 1200 | "universalify": "^2.0.0" 1201 | } 1202 | }, 1203 | "fsevents": { 1204 | "version": "2.3.2", 1205 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1206 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1207 | "dev": true, 1208 | "optional": true 1209 | }, 1210 | "get-caller-file": { 1211 | "version": "2.0.5", 1212 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1213 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1214 | "dev": true 1215 | }, 1216 | "get-stdin": { 1217 | "version": "9.0.0", 1218 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", 1219 | "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", 1220 | "dev": true 1221 | }, 1222 | "glob-parent": { 1223 | "version": "5.1.2", 1224 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1225 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1226 | "dev": true, 1227 | "requires": { 1228 | "is-glob": "^4.0.1" 1229 | } 1230 | }, 1231 | "globby": { 1232 | "version": "13.1.2", 1233 | "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", 1234 | "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", 1235 | "dev": true, 1236 | "requires": { 1237 | "dir-glob": "^3.0.1", 1238 | "fast-glob": "^3.2.11", 1239 | "ignore": "^5.2.0", 1240 | "merge2": "^1.4.1", 1241 | "slash": "^4.0.0" 1242 | } 1243 | }, 1244 | "graceful-fs": { 1245 | "version": "4.2.10", 1246 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", 1247 | "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", 1248 | "dev": true 1249 | }, 1250 | "ignore": { 1251 | "version": "5.2.0", 1252 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 1253 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", 1254 | "dev": true 1255 | }, 1256 | "is-binary-path": { 1257 | "version": "2.1.0", 1258 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1259 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1260 | "dev": true, 1261 | "requires": { 1262 | "binary-extensions": "^2.0.0" 1263 | } 1264 | }, 1265 | "is-extglob": { 1266 | "version": "2.1.1", 1267 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1268 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1269 | "dev": true 1270 | }, 1271 | "is-fullwidth-code-point": { 1272 | "version": "3.0.0", 1273 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1274 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1275 | "dev": true 1276 | }, 1277 | "is-glob": { 1278 | "version": "4.0.3", 1279 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1280 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1281 | "dev": true, 1282 | "requires": { 1283 | "is-extglob": "^2.1.1" 1284 | } 1285 | }, 1286 | "is-number": { 1287 | "version": "7.0.0", 1288 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1289 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1290 | "dev": true 1291 | }, 1292 | "jsonfile": { 1293 | "version": "6.1.0", 1294 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", 1295 | "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", 1296 | "dev": true, 1297 | "requires": { 1298 | "graceful-fs": "^4.1.6", 1299 | "universalify": "^2.0.0" 1300 | } 1301 | }, 1302 | "lilconfig": { 1303 | "version": "2.0.6", 1304 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", 1305 | "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", 1306 | "dev": true 1307 | }, 1308 | "merge2": { 1309 | "version": "1.4.1", 1310 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1311 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 1312 | "dev": true 1313 | }, 1314 | "micromatch": { 1315 | "version": "4.0.5", 1316 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 1317 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 1318 | "dev": true, 1319 | "requires": { 1320 | "braces": "^3.0.2", 1321 | "picomatch": "^2.3.1" 1322 | } 1323 | }, 1324 | "nanoid": { 1325 | "version": "3.3.4", 1326 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 1327 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", 1328 | "dev": true, 1329 | "peer": true 1330 | }, 1331 | "node-releases": { 1332 | "version": "2.0.6", 1333 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", 1334 | "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", 1335 | "dev": true 1336 | }, 1337 | "normalize-path": { 1338 | "version": "3.0.0", 1339 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1340 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1341 | "dev": true 1342 | }, 1343 | "normalize-range": { 1344 | "version": "0.1.2", 1345 | "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", 1346 | "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", 1347 | "dev": true 1348 | }, 1349 | "path-type": { 1350 | "version": "4.0.0", 1351 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 1352 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 1353 | "dev": true 1354 | }, 1355 | "picocolors": { 1356 | "version": "1.0.0", 1357 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1358 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 1359 | "dev": true 1360 | }, 1361 | "picomatch": { 1362 | "version": "2.3.1", 1363 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1364 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1365 | "dev": true 1366 | }, 1367 | "pify": { 1368 | "version": "2.3.0", 1369 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1370 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 1371 | "dev": true 1372 | }, 1373 | "postcss": { 1374 | "version": "8.4.16", 1375 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", 1376 | "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", 1377 | "dev": true, 1378 | "peer": true, 1379 | "requires": { 1380 | "nanoid": "^3.3.4", 1381 | "picocolors": "^1.0.0", 1382 | "source-map-js": "^1.0.2" 1383 | } 1384 | }, 1385 | "postcss-cli": { 1386 | "version": "10.0.0", 1387 | "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-10.0.0.tgz", 1388 | "integrity": "sha512-Wjy/00wBBEgQqnSToznxLWDnATznokFGXsHtF/3G8glRZpz5KYlfHcBW/VMJmWAeF2x49zjgy4izjM3/Wx1dKA==", 1389 | "dev": true, 1390 | "requires": { 1391 | "chokidar": "^3.3.0", 1392 | "dependency-graph": "^0.11.0", 1393 | "fs-extra": "^10.0.0", 1394 | "get-stdin": "^9.0.0", 1395 | "globby": "^13.0.0", 1396 | "picocolors": "^1.0.0", 1397 | "postcss-load-config": "^4.0.0", 1398 | "postcss-reporter": "^7.0.0", 1399 | "pretty-hrtime": "^1.0.3", 1400 | "read-cache": "^1.0.0", 1401 | "slash": "^4.0.0", 1402 | "yargs": "^17.0.0" 1403 | } 1404 | }, 1405 | "postcss-custom-properties": { 1406 | "version": "12.1.8", 1407 | "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", 1408 | "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", 1409 | "dev": true, 1410 | "requires": { 1411 | "postcss-value-parser": "^4.2.0" 1412 | } 1413 | }, 1414 | "postcss-load-config": { 1415 | "version": "4.0.1", 1416 | "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", 1417 | "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", 1418 | "dev": true, 1419 | "requires": { 1420 | "lilconfig": "^2.0.5", 1421 | "yaml": "^2.1.1" 1422 | } 1423 | }, 1424 | "postcss-reporter": { 1425 | "version": "7.0.5", 1426 | "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.0.5.tgz", 1427 | "integrity": "sha512-glWg7VZBilooZGOFPhN9msJ3FQs19Hie7l5a/eE6WglzYqVeH3ong3ShFcp9kDWJT1g2Y/wd59cocf9XxBtkWA==", 1428 | "dev": true, 1429 | "requires": { 1430 | "picocolors": "^1.0.0", 1431 | "thenby": "^1.3.4" 1432 | } 1433 | }, 1434 | "postcss-value-parser": { 1435 | "version": "4.2.0", 1436 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 1437 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 1438 | "dev": true 1439 | }, 1440 | "pretty-hrtime": { 1441 | "version": "1.0.3", 1442 | "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", 1443 | "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", 1444 | "dev": true 1445 | }, 1446 | "queue-microtask": { 1447 | "version": "1.2.3", 1448 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1449 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1450 | "dev": true 1451 | }, 1452 | "read-cache": { 1453 | "version": "1.0.0", 1454 | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 1455 | "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 1456 | "dev": true, 1457 | "requires": { 1458 | "pify": "^2.3.0" 1459 | } 1460 | }, 1461 | "readdirp": { 1462 | "version": "3.6.0", 1463 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1464 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1465 | "dev": true, 1466 | "requires": { 1467 | "picomatch": "^2.2.1" 1468 | } 1469 | }, 1470 | "require-directory": { 1471 | "version": "2.1.1", 1472 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1473 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1474 | "dev": true 1475 | }, 1476 | "reusify": { 1477 | "version": "1.0.4", 1478 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1479 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1480 | "dev": true 1481 | }, 1482 | "run-parallel": { 1483 | "version": "1.2.0", 1484 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1485 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1486 | "dev": true, 1487 | "requires": { 1488 | "queue-microtask": "^1.2.2" 1489 | } 1490 | }, 1491 | "slash": { 1492 | "version": "4.0.0", 1493 | "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", 1494 | "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", 1495 | "dev": true 1496 | }, 1497 | "source-map-js": { 1498 | "version": "1.0.2", 1499 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1500 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 1501 | "dev": true, 1502 | "peer": true 1503 | }, 1504 | "string-width": { 1505 | "version": "4.2.3", 1506 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1507 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1508 | "dev": true, 1509 | "requires": { 1510 | "emoji-regex": "^8.0.0", 1511 | "is-fullwidth-code-point": "^3.0.0", 1512 | "strip-ansi": "^6.0.1" 1513 | } 1514 | }, 1515 | "strip-ansi": { 1516 | "version": "6.0.1", 1517 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1518 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1519 | "dev": true, 1520 | "requires": { 1521 | "ansi-regex": "^5.0.1" 1522 | } 1523 | }, 1524 | "thenby": { 1525 | "version": "1.3.4", 1526 | "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", 1527 | "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", 1528 | "dev": true 1529 | }, 1530 | "to-regex-range": { 1531 | "version": "5.0.1", 1532 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1533 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1534 | "dev": true, 1535 | "requires": { 1536 | "is-number": "^7.0.0" 1537 | } 1538 | }, 1539 | "universalify": { 1540 | "version": "2.0.0", 1541 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", 1542 | "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", 1543 | "dev": true 1544 | }, 1545 | "update-browserslist-db": { 1546 | "version": "1.0.7", 1547 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.7.tgz", 1548 | "integrity": "sha512-iN/XYesmZ2RmmWAiI4Z5rq0YqSiv0brj9Ce9CfhNE4xIW2h+MFxcgkxIzZ+ShkFPUkjU3gQ+3oypadD3RAMtrg==", 1549 | "dev": true, 1550 | "requires": { 1551 | "escalade": "^3.1.1", 1552 | "picocolors": "^1.0.0" 1553 | } 1554 | }, 1555 | "wrap-ansi": { 1556 | "version": "7.0.0", 1557 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1558 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1559 | "dev": true, 1560 | "requires": { 1561 | "ansi-styles": "^4.0.0", 1562 | "string-width": "^4.1.0", 1563 | "strip-ansi": "^6.0.0" 1564 | } 1565 | }, 1566 | "y18n": { 1567 | "version": "5.0.8", 1568 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1569 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1570 | "dev": true 1571 | }, 1572 | "yaml": { 1573 | "version": "2.1.1", 1574 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", 1575 | "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", 1576 | "dev": true 1577 | }, 1578 | "yargs": { 1579 | "version": "17.5.1", 1580 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", 1581 | "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", 1582 | "dev": true, 1583 | "requires": { 1584 | "cliui": "^7.0.2", 1585 | "escalade": "^3.1.1", 1586 | "get-caller-file": "^2.0.5", 1587 | "require-directory": "^2.1.1", 1588 | "string-width": "^4.2.3", 1589 | "y18n": "^5.0.5", 1590 | "yargs-parser": "^21.0.0" 1591 | } 1592 | }, 1593 | "yargs-parser": { 1594 | "version": "21.1.1", 1595 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 1596 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 1597 | "dev": true 1598 | } 1599 | } 1600 | } 1601 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phpapprentice", 3 | "version": "1.0.0", 4 | "description": "A website for learning PHP", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/restoreddev/phpapprentice.git" 11 | }, 12 | "author": "", 13 | "license": "MIT", 14 | "bugs": { 15 | "url": "https://github.com/restoreddev/phpapprentice/issues" 16 | }, 17 | "homepage": "https://github.com/restoreddev/phpapprentice#readme", 18 | "devDependencies": { 19 | "autoprefixer": "^10.4.8", 20 | "postcss-cli": "^10.0.0", 21 | "postcss-custom-properties": "^12.0.0" 22 | }, 23 | "dependencies": {} 24 | } 25 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-custom-properties'), 4 | require('autoprefixer'), 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /static/arrow-thin-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/arrow-thin-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/arrow-thin-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/arrow-thin-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/book-reference.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/cheveron-outline-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/cheveron-outline-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/cheveron-outline-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/cheveron-outline-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/close-outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/close-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/elephant.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | elephant 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /static/favicon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/restoreddev/phpapprentice/dc3082d386e40f12d9b6c153740f1fa23e30cdce/static/favicon-32.png -------------------------------------------------------------------------------- /static/list-bullet.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/menu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/show-sidebar.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------