├── .gitignore ├── CNAME ├── CONTRIBUTING.md ├── Gemfile ├── Gemfile.lock ├── Gruntfile.js ├── LICENSE ├── README.md ├── _config.yml ├── _includes └── welcome.md ├── _layouts ├── default.html └── page.html ├── _posts ├── 01-01-01-Getting-Started.md ├── 01-02-01-Use-the-Current-Stable-Version.md ├── 01-03-01-Built-in-Web-Server.md ├── 01-04-01-Mac-Setup.md ├── 01-05-01-Windows-Setup.md ├── 01-06-01-Common-Directory-Structure.md ├── 02-01-01-Code-Style-Guide.md ├── 03-01-01-Language-Highlights.md ├── 03-02-01-Programming-Paradigms.md ├── 03-03-01-Namespaces.md ├── 03-04-01-Standard-PHP-Library.md ├── 03-05-01-Command-Line-Interface.md ├── 03-06-01-XDebug.md ├── 04-01-01-Dependency-Management.md ├── 04-02-01-Composer-and-Packagist.md ├── 04-03-01-PEAR.md ├── 05-01-01-Coding-Practices.md ├── 05-02-01-The-Basics.md ├── 05-03-01-Date-and-Time.md ├── 05-04-01-Design-Patterns.md ├── 05-05-01-PHP-and-UTF8.md ├── 05-06-01-Internationalization-and-Localization.md ├── 06-01-01-Dependency-Injection.md ├── 06-02-01-Basic-Concept.md ├── 06-03-01-Complex-Problem.md ├── 06-04-01-Containers.md ├── 06-05-01-Further-Reading.md ├── 07-01-01-Databases.md ├── 07-02-01-Databases_MySQL.md ├── 07-03-01-Databases_PDO.md ├── 07-04-01-Interacting-via-Code.md ├── 07-05-01-Abstraction-Layers.md ├── 08-01-01-Templating.md ├── 08-02-01-Benefits.md ├── 08-03-01-Plain-PHP-Templates.md ├── 08-04-01-Compiled-Templates.md ├── 08-05-01-Further-Reading.md ├── 09-01-01-Errors-and-Exceptions.md ├── 09-02-01-Errors.md ├── 09-03-01-Exceptions.md ├── 10-01-01-Security.md ├── 10-02-01-Web-Application-Security.md ├── 10-03-01-Password-Hashing.md ├── 10-04-01-Data-Filtering.md ├── 10-05-01-Configuration-Files.md ├── 10-06-01-Register-Globals.md ├── 10-07-01-Error-Reporting.md ├── 11-01-01-Testing.md ├── 11-02-01-Test-Driven-Development.md ├── 11-03-01-Behavior-Driven-Development.md ├── 11-04-01-Complementary-Testing-Tools.md ├── 12-01-01-Servers-and-Deployment.md ├── 12-02-01-Platform-as-a-Service.md ├── 12-03-01-Virtual-or-Dedicated-Servers.md ├── 12-04-01-Shared-Servers.md ├── 12-05-01-Building-your-Application.md ├── 13-01-01-Virtualization.md ├── 13-02-01-Vagrant.md ├── 13-03-01-Docker.md ├── 14-01-01-Caching.md ├── 14-02-01-Opcode-Cache.md ├── 14-03-01-Object-Caching.md ├── 15-01-01-Documenting.md ├── 15-02-01-PHPDoc.md ├── 16-01-01-Resources.md ├── 16-02-01-From-the-Source.md ├── 16-03-01-People-to-Follow.md ├── 16-04-01-Mentoring.md ├── 16-05-01-PHP-PaaS-Providers.md ├── 16-06-01-Frameworks.md ├── 16-07-01-Components.md ├── 16-08-01-Sites.md ├── 16-09-01-Videos.md ├── 16-10-01-Books.md ├── 17-01-01-Community.md ├── 17-02-01-User-Groups.md ├── 17-03-01-Conferences.md └── 17-04-01-Elephpants.md ├── banners.md ├── css └── all.css ├── images ├── banners │ ├── btn1-120x90.png │ ├── btn2-120x60.png │ ├── leaderboard-728x90.png │ ├── lg-rect-386x280.png │ ├── med-rect-300x250.png │ ├── rect-180x150.png │ ├── sq-btn-125x125.png │ └── vert-rect-240x400.png ├── favicon.png ├── nmc-logo.gif ├── og-image.png └── og-logo.png ├── index.html ├── less └── all.less ├── package.json ├── pages ├── Design-Patterns.md ├── Functional-Programming.md ├── The-Basics.md └── example.md ├── scripts ├── fastclick.js └── setup.js └── styles ├── all.css ├── all.less ├── base ├── all.less ├── bars-buttons.less ├── buttons.less ├── grid.less ├── idioms.less ├── prefixer.less ├── reset.less ├── spacing.less ├── typography.less └── variables.less ├── print.css ├── site ├── site-content.less ├── site-footer.less ├── site-header.less ├── site-navigation.less └── variables.less └── syntax.css /.gitignore: -------------------------------------------------------------------------------- 1 | /_site/ 2 | *.DS_Store 3 | node_modules 4 | vendor 5 | .bundle 6 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | ja.phptherightway.com 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to PHP The Right Way 2 | 3 | Enjoy [PHP The Right Way](https://phptherightway.com) and want to get 4 | involved? Great! There are plenty of ways you can help out. 5 | 6 | Please take a moment to review this document in order to make the contribution 7 | process easy and effective for everyone involved. 8 | 9 | Following these guidelines helps to communicate that you respect the time of 10 | the developers managing and developing this open source project. In return, 11 | they should reciprocate that respect in addressing your issue or assessing 12 | patches and features. 13 | 14 | 15 | ## Using the issue tracker 16 | 17 | The [issue tracker](https://github.com/codeguy/php-the-right-way/issues) is 18 | the preferred channel for changes: spelling mistakes, wording changes, new 19 | content and generally [submitting pull requests](#pull-requests), but please 20 | respect the following restrictions: 21 | 22 | * Please **do not** use the issue tracker for personal support requests (use 23 | [Stack Overflow](https://stackoverflow.com/questions/tagged/php) or IRC). 24 | 25 | * Please **do not** derail or troll issues. Keep the discussion on topic and 26 | respect the opinions of others. 27 | 28 | 29 | 30 | ## Pull Requests 31 | 32 | Pull requests are a great way to add new content to PHP The Right Way, as well 33 | as updating any browser issues or other style changes. Pretty much any sort of 34 | change is accepted if seen as constructive. 35 | 36 | Adhering to the following process is the best way to get your work 37 | included in the project: 38 | 39 | 1. [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the 40 | project, clone your fork, and configure the remotes: 41 | 42 | ```bash 43 | # Clone your fork of the repo into the current directory 44 | git clone https://github.com//php-the-right-way.git 45 | # Navigate to the newly cloned directory 46 | cd php-the-right-way 47 | # Assign the original repo to a remote called "upstream" 48 | git remote add upstream https://github.com/codeguy/php-the-right-way.git 49 | ``` 50 | 51 | 2. If you cloned a while ago, get the latest changes from upstream: 52 | 53 | ```bash 54 | git checkout gh-pages 55 | git pull upstream gh-pages 56 | ``` 57 | 58 | 3. Create a new topic branch (off the main project development branch) to 59 | contain your change or fix: 60 | 61 | ```bash 62 | git checkout -b 63 | ``` 64 | 65 | 4. Install the [Jekyll](https://github.com/jekyll/jekyll/) gem and dependencies to preview locally: 66 | 67 | ```bash 68 | # Install the needed gems through Bundler 69 | bundle install --path vendor/bundle 70 | # Run the local server 71 | bundle exec jekyll serve 72 | ``` 73 | 74 | 5. Commit your changes in logical chunks. Please adhere to these [git commit 75 | message guidelines](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 76 | or your content is unlikely be merged into the main project. Use Git's 77 | [interactive rebase](https://docs.github.com/en/get-started/using-git/about-git-rebase) 78 | feature to tidy up your commits before making them public. 79 | 80 | 6. Locally merge (or rebase) the upstream development branch into your topic branch: 81 | 82 | ```bash 83 | git pull [--rebase] upstream gh-pages 84 | ``` 85 | 86 | 7. Push your topic branch up to your fork: 87 | 88 | ```bash 89 | git push origin 90 | ``` 91 | 92 | 8. [Open a Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) 93 | with a clear title and description. 94 | 95 | 96 | ## Contribution Agreement and Usage 97 | 98 | By submitting a pull request to this repository, you agree to allow the project 99 | owners to license your work under the the terms of the [Creative Commons Attribution-NonCommercial-ShareAlike 100 | 3.0 Unported License](https://creativecommons.org/licenses/by-nc-sa/3.0/). 101 | 102 | The same content and license will be used for all PHP The Right Way publications, 103 | including - but not limited to: 104 | 105 | * [phptherightway.com](https://phptherightway.com) 106 | * Translations of phptherightway.com 107 | * [LeanPub: PHP The Right Way](https://leanpub.com/phptherightway/) 108 | * Translations of "LeanPub: PHP The Right Way" 109 | 110 | All content is completely free now, and always will be. 111 | 112 | ## Contributor Style Guide 113 | 114 | 1. Use American English spelling (*primary English repo only*) 115 | 2. Use four (4) spaces to indent text; do not use tabs 116 | 3. Wrap all text to 120 characters 117 | 4. Code samples should adhere to PSR-1 or higher 118 | 5. Use [GitHub Flavored Markdown](https://github.github.com/gfm/) for all content 119 | 6. Use language agnostic urls when referring to external websites such as the [php.net](https://www.php.net/urlhowto.php) manual 120 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'github-pages', group: :jekyll_plugins 3 | gem 'rouge' 4 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (6.0.3.5) 5 | concurrent-ruby (~> 1.0, >= 1.0.2) 6 | i18n (>= 0.7, < 2) 7 | minitest (~> 5.1) 8 | tzinfo (~> 1.1) 9 | zeitwerk (~> 2.2, >= 2.2.2) 10 | addressable (2.7.0) 11 | public_suffix (>= 2.0.2, < 5.0) 12 | coffee-script (2.4.1) 13 | coffee-script-source 14 | execjs 15 | coffee-script-source (1.11.1) 16 | colorator (1.1.0) 17 | commonmarker (0.17.13) 18 | ruby-enum (~> 0.5) 19 | concurrent-ruby (1.1.8) 20 | dnsruby (1.61.5) 21 | simpleidn (~> 0.1) 22 | em-websocket (0.5.2) 23 | eventmachine (>= 0.12.9) 24 | http_parser.rb (~> 0.6.0) 25 | ethon (0.12.0) 26 | ffi (>= 1.3.0) 27 | eventmachine (1.2.7) 28 | execjs (2.7.0) 29 | faraday (1.3.0) 30 | faraday-net_http (~> 1.0) 31 | multipart-post (>= 1.2, < 3) 32 | ruby2_keywords 33 | faraday-net_http (1.0.1) 34 | ffi (1.15.0) 35 | forwardable-extended (2.6.0) 36 | gemoji (3.0.1) 37 | github-pages (213) 38 | github-pages-health-check (= 1.17.0) 39 | jekyll (= 3.9.0) 40 | jekyll-avatar (= 0.7.0) 41 | jekyll-coffeescript (= 1.1.1) 42 | jekyll-commonmark-ghpages (= 0.1.6) 43 | jekyll-default-layout (= 0.1.4) 44 | jekyll-feed (= 0.15.1) 45 | jekyll-gist (= 1.5.0) 46 | jekyll-github-metadata (= 2.13.0) 47 | jekyll-mentions (= 1.6.0) 48 | jekyll-optional-front-matter (= 0.3.2) 49 | jekyll-paginate (= 1.1.0) 50 | jekyll-readme-index (= 0.3.0) 51 | jekyll-redirect-from (= 0.16.0) 52 | jekyll-relative-links (= 0.6.1) 53 | jekyll-remote-theme (= 0.4.3) 54 | jekyll-sass-converter (= 1.5.2) 55 | jekyll-seo-tag (= 2.7.1) 56 | jekyll-sitemap (= 1.4.0) 57 | jekyll-swiss (= 1.0.0) 58 | jekyll-theme-architect (= 0.1.1) 59 | jekyll-theme-cayman (= 0.1.1) 60 | jekyll-theme-dinky (= 0.1.1) 61 | jekyll-theme-hacker (= 0.1.2) 62 | jekyll-theme-leap-day (= 0.1.1) 63 | jekyll-theme-merlot (= 0.1.1) 64 | jekyll-theme-midnight (= 0.1.1) 65 | jekyll-theme-minimal (= 0.1.1) 66 | jekyll-theme-modernist (= 0.1.1) 67 | jekyll-theme-primer (= 0.5.4) 68 | jekyll-theme-slate (= 0.1.1) 69 | jekyll-theme-tactile (= 0.1.1) 70 | jekyll-theme-time-machine (= 0.1.1) 71 | jekyll-titles-from-headings (= 0.5.3) 72 | jemoji (= 0.12.0) 73 | kramdown (= 2.3.0) 74 | kramdown-parser-gfm (= 1.1.0) 75 | liquid (= 4.0.3) 76 | mercenary (~> 0.3) 77 | minima (= 2.5.1) 78 | nokogiri (>= 1.10.4, < 2.0) 79 | rouge (= 3.26.0) 80 | terminal-table (~> 1.4) 81 | github-pages-health-check (1.17.0) 82 | addressable (~> 2.3) 83 | dnsruby (~> 1.60) 84 | octokit (~> 4.0) 85 | public_suffix (>= 2.0.2, < 5.0) 86 | typhoeus (~> 1.3) 87 | html-pipeline (2.14.0) 88 | activesupport (>= 2) 89 | nokogiri (>= 1.4) 90 | http_parser.rb (0.6.0) 91 | i18n (0.9.5) 92 | concurrent-ruby (~> 1.0) 93 | jekyll (3.9.0) 94 | addressable (~> 2.4) 95 | colorator (~> 1.0) 96 | em-websocket (~> 0.5) 97 | i18n (~> 0.7) 98 | jekyll-sass-converter (~> 1.0) 99 | jekyll-watch (~> 2.0) 100 | kramdown (>= 1.17, < 3) 101 | liquid (~> 4.0) 102 | mercenary (~> 0.3.3) 103 | pathutil (~> 0.9) 104 | rouge (>= 1.7, < 4) 105 | safe_yaml (~> 1.0) 106 | jekyll-avatar (0.7.0) 107 | jekyll (>= 3.0, < 5.0) 108 | jekyll-coffeescript (1.1.1) 109 | coffee-script (~> 2.2) 110 | coffee-script-source (~> 1.11.1) 111 | jekyll-commonmark (1.3.1) 112 | commonmarker (~> 0.14) 113 | jekyll (>= 3.7, < 5.0) 114 | jekyll-commonmark-ghpages (0.1.6) 115 | commonmarker (~> 0.17.6) 116 | jekyll-commonmark (~> 1.2) 117 | rouge (>= 2.0, < 4.0) 118 | jekyll-default-layout (0.1.4) 119 | jekyll (~> 3.0) 120 | jekyll-feed (0.15.1) 121 | jekyll (>= 3.7, < 5.0) 122 | jekyll-gist (1.5.0) 123 | octokit (~> 4.2) 124 | jekyll-github-metadata (2.13.0) 125 | jekyll (>= 3.4, < 5.0) 126 | octokit (~> 4.0, != 4.4.0) 127 | jekyll-mentions (1.6.0) 128 | html-pipeline (~> 2.3) 129 | jekyll (>= 3.7, < 5.0) 130 | jekyll-optional-front-matter (0.3.2) 131 | jekyll (>= 3.0, < 5.0) 132 | jekyll-paginate (1.1.0) 133 | jekyll-readme-index (0.3.0) 134 | jekyll (>= 3.0, < 5.0) 135 | jekyll-redirect-from (0.16.0) 136 | jekyll (>= 3.3, < 5.0) 137 | jekyll-relative-links (0.6.1) 138 | jekyll (>= 3.3, < 5.0) 139 | jekyll-remote-theme (0.4.3) 140 | addressable (~> 2.0) 141 | jekyll (>= 3.5, < 5.0) 142 | jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) 143 | rubyzip (>= 1.3.0, < 3.0) 144 | jekyll-sass-converter (1.5.2) 145 | sass (~> 3.4) 146 | jekyll-seo-tag (2.7.1) 147 | jekyll (>= 3.8, < 5.0) 148 | jekyll-sitemap (1.4.0) 149 | jekyll (>= 3.7, < 5.0) 150 | jekyll-swiss (1.0.0) 151 | jekyll-theme-architect (0.1.1) 152 | jekyll (~> 3.5) 153 | jekyll-seo-tag (~> 2.0) 154 | jekyll-theme-cayman (0.1.1) 155 | jekyll (~> 3.5) 156 | jekyll-seo-tag (~> 2.0) 157 | jekyll-theme-dinky (0.1.1) 158 | jekyll (~> 3.5) 159 | jekyll-seo-tag (~> 2.0) 160 | jekyll-theme-hacker (0.1.2) 161 | jekyll (> 3.5, < 5.0) 162 | jekyll-seo-tag (~> 2.0) 163 | jekyll-theme-leap-day (0.1.1) 164 | jekyll (~> 3.5) 165 | jekyll-seo-tag (~> 2.0) 166 | jekyll-theme-merlot (0.1.1) 167 | jekyll (~> 3.5) 168 | jekyll-seo-tag (~> 2.0) 169 | jekyll-theme-midnight (0.1.1) 170 | jekyll (~> 3.5) 171 | jekyll-seo-tag (~> 2.0) 172 | jekyll-theme-minimal (0.1.1) 173 | jekyll (~> 3.5) 174 | jekyll-seo-tag (~> 2.0) 175 | jekyll-theme-modernist (0.1.1) 176 | jekyll (~> 3.5) 177 | jekyll-seo-tag (~> 2.0) 178 | jekyll-theme-primer (0.5.4) 179 | jekyll (> 3.5, < 5.0) 180 | jekyll-github-metadata (~> 2.9) 181 | jekyll-seo-tag (~> 2.0) 182 | jekyll-theme-slate (0.1.1) 183 | jekyll (~> 3.5) 184 | jekyll-seo-tag (~> 2.0) 185 | jekyll-theme-tactile (0.1.1) 186 | jekyll (~> 3.5) 187 | jekyll-seo-tag (~> 2.0) 188 | jekyll-theme-time-machine (0.1.1) 189 | jekyll (~> 3.5) 190 | jekyll-seo-tag (~> 2.0) 191 | jekyll-titles-from-headings (0.5.3) 192 | jekyll (>= 3.3, < 5.0) 193 | jekyll-watch (2.2.1) 194 | listen (~> 3.0) 195 | jemoji (0.12.0) 196 | gemoji (~> 3.0) 197 | html-pipeline (~> 2.2) 198 | jekyll (>= 3.0, < 5.0) 199 | kramdown (2.3.0) 200 | rexml 201 | kramdown-parser-gfm (1.1.0) 202 | kramdown (~> 2.0) 203 | liquid (4.0.3) 204 | listen (3.4.1) 205 | rb-fsevent (~> 0.10, >= 0.10.3) 206 | rb-inotify (~> 0.9, >= 0.9.10) 207 | mercenary (0.3.6) 208 | mini_portile2 (2.5.0) 209 | minima (2.5.1) 210 | jekyll (>= 3.5, < 5.0) 211 | jekyll-feed (~> 0.9) 212 | jekyll-seo-tag (~> 2.1) 213 | minitest (5.14.4) 214 | multipart-post (2.1.1) 215 | nokogiri (1.11.2) 216 | mini_portile2 (~> 2.5.0) 217 | racc (~> 1.4) 218 | octokit (4.20.0) 219 | faraday (>= 0.9) 220 | sawyer (~> 0.8.0, >= 0.5.3) 221 | pathutil (0.16.2) 222 | forwardable-extended (~> 2.6) 223 | public_suffix (4.0.6) 224 | racc (1.5.2) 225 | rb-fsevent (0.10.4) 226 | rb-inotify (0.10.1) 227 | ffi (~> 1.0) 228 | rexml (3.2.4) 229 | rouge (3.26.0) 230 | ruby-enum (0.9.0) 231 | i18n 232 | ruby2_keywords (0.0.4) 233 | rubyzip (2.3.0) 234 | safe_yaml (1.0.5) 235 | sass (3.7.4) 236 | sass-listen (~> 4.0.0) 237 | sass-listen (4.0.0) 238 | rb-fsevent (~> 0.9, >= 0.9.4) 239 | rb-inotify (~> 0.9, >= 0.9.7) 240 | sawyer (0.8.2) 241 | addressable (>= 2.3.5) 242 | faraday (> 0.8, < 2.0) 243 | simpleidn (0.2.1) 244 | unf (~> 0.1.4) 245 | terminal-table (1.8.0) 246 | unicode-display_width (~> 1.1, >= 1.1.1) 247 | thread_safe (0.3.6) 248 | typhoeus (1.4.0) 249 | ethon (>= 0.9.0) 250 | tzinfo (1.2.9) 251 | thread_safe (~> 0.1) 252 | unf (0.1.4) 253 | unf_ext 254 | unf_ext (0.0.7.7) 255 | unicode-display_width (1.7.0) 256 | zeitwerk (2.4.2) 257 | 258 | PLATFORMS 259 | ruby 260 | 261 | DEPENDENCIES 262 | github-pages 263 | rouge 264 | 265 | BUNDLED WITH 266 | 2.2.5 267 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | // Project configuration 3 | grunt.initConfig({ 4 | pkg: grunt.file.readJSON('package.json'), 5 | less: { 6 | dist: { 7 | options: { 8 | cleancss: true, 9 | compress: true, 10 | ieCompat: true 11 | }, 12 | files: { 13 | "css/all.css": "less/all.less" 14 | } 15 | } 16 | }, 17 | postcss: { 18 | options: { 19 | map: true, 20 | processors: [ 21 | require('autoprefixer')({ 22 | browsers: ['last 2 versions', 'ie 9'] 23 | }) 24 | ] 25 | }, 26 | dist: { 27 | src: 'css/all.css' 28 | } 29 | }, 30 | watch: { 31 | less: { 32 | files: ['less/**/*.less'], 33 | tasks: ['less:dist', 'postcss:dist'], 34 | options: { 35 | spawn: false 36 | } 37 | } 38 | } 39 | }); 40 | 41 | // Load plugins 42 | grunt.loadNpmTasks('grunt-contrib-less'); 43 | grunt.loadNpmTasks('grunt-contrib-watch'); 44 | grunt.loadNpmTasks('grunt-postcss'); 45 | 46 | // Default task(s) 47 | grunt.registerTask('default', ['less', 'postcss:dist']); 48 | }; 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Josh Lockhart 2 | 3 | https://creativecommons.org/licenses/by-nc-sa/3.0/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP: The Right Way 2 | 3 | ## Notes for Japanese translation 4 | 5 | それなりに話題になってみたいで、やたらブックマークされてるけれど、内容に関するコメントをこの週末までほとんど見かけなかった。 6 | ので、とりあえずざっと読んで訳してみました。 7 | 8 | 翻訳に関するツッコミは大歓迎。 9 | 10 | そもそも原文の時点でどうなのよ、というところがあればぜひ原文のほうにフィードバックを :-) 11 | 12 | ## Overview 13 | 14 | This is the GitHub Pages repository for the _PHP: The Right Way_ project. 15 | 16 | * This website is a Jekyll project. 17 | * Each section and sub-section are a Markdown file in `_posts/`. 18 | * Sub-sections have `isChild: true` in their front matter. 19 | * The navigation and page structure are automatically generated. 20 | 21 | ## Spread the Word! 22 | 23 | _PHP: The Right Way_ has web banner images you can use on your website. Show your support, and let new PHP 24 | developers know where to find good information! 25 | 26 | [See Banner Images](https://www.phptherightway.com/banners.html) 27 | 28 | ## How to Contribute 29 | 30 | You should read the `CONTRIBUTING.md` file for precise instructions and tips. But, if you prefer a TL;DR: 31 | 32 | 1. Fork and edit 33 | 2. Optionally install [Ruby](https://rvm.io/rvm/install/) with [Jekyll](https://github.com/mojombo/jekyll/) gem to preview locally 34 | 3. Submit pull request for consideration 35 | 36 | ### Contributor Style Guide 37 | 38 | 1. Use American English spelling (*primary English repo only*). 39 | 2. Use four (4) spaces to indent text; do not use tabs. 40 | 3. Wrap all text to 120 characters. 41 | 4. Code samples should adhere to PSR-1 or higher. 42 | 43 | ## Where 44 | 45 | 46 | 47 | * [English](https://www.phptherightway.com) 48 | * [Deutsch](https://rwetzlmayr.github.io/php-the-right-way) 49 | * [Español](https://phpdevenezuela.github.io/php-the-right-way) 50 | * [Français](https://eilgin.github.io/php-the-right-way/) 51 | * [Indonesia](https://id.phptherightway.com) 52 | * [Italiano](https://it.phptherightway.com) 53 | * [Polski](https://pl.phptherightway.com) 54 | * [Português do Brasil](https://br.phptherightway.com) 55 | * [Română](https://bgui.github.io/php-the-right-way/) 56 | * [Slovenščina](https://sl.phptherightway.com) 57 | * [Srpski](https://phpsrbija.github.io/php-the-right-way/) 58 | * [Türkçe](https://hkulekci.github.io/php-the-right-way/) 59 | * [български](https://bg.phptherightway.com) 60 | * [Русский язык](https://getjump.github.io/ru-php-the-right-way) 61 | * [Українська](https://iflista.github.io/php-the-right-way/) 62 | * [العربية](https://adaroobi.github.io/php-the-right-way/) 63 | * [فارسى](https://novid.github.io/php-the-right-way/) 64 | * [ภาษาไทย](https://apzentral.github.io/php-the-right-way/) 65 | * [한국어판](https://modernpug.github.io/php-the-right-way) 66 | * [日本語](https://ja.phptherightway.com) 67 | * [简体中文](https://laravel-china.github.io/php-the-right-way/) 68 | * [繁體中文](https://laravel-taiwan.github.io/php-the-right-way) 69 | 70 | ### Translations 71 | 72 | If you are interested in translating _PHP: The Right Way_, fork this repo on GitHub and publish your localized fork to your own GitHub Pages account. We'll link to your translation from the primary document. 73 | 74 | To avoid fragmentation and reader confusion, please choose one of these options: 75 | 76 | 1. We link to your GitHub Pages fork with `[username].github.io/php-the-right-way` 77 | 2. We link to your GitHub Pages fork with a subdomain (e.g. "ru.phptherightway.com") 78 | 79 | If you use a subdomain, enter the subdomain into the `CNAME` file, and ask us to setup DNS for you. If you do not use a subdomain, remove the `CNAME` file entirely else your fork will not build when pushed. 80 | 81 | Add information about your translation in the [wiki page](https://github.com/codeguy/php-the-right-way/wiki/Translations). 82 | 83 | When your translation is ready, open an issue on the Issue Tracker to let us know. 84 | 85 | ## Why 86 | 87 | There's been a lot of discussion lately about how the PHP community lacks sufficient, credible information for programmers new to PHP. This repository aims to solve this problem. 88 | 89 | ## Who 90 | 91 | My name is [Josh Lockhart](https://twitter.com/codeguy). I'm the author of the [Slim Framework](https://www.slimframework.com/), and I work for [New Media Campaigns](https://www.newmediacampaigns.com/). 92 | 93 | ### Collaborators 94 | 95 | * [Kris Jordan](https://krisjordan.com/) 96 | * [Phil Sturgeon](https://phil.tech/) 97 | 98 | ## License 99 | 100 | [Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License](https://creativecommons.org/licenses/by-nc-sa/3.0/) 101 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | baseurl: / 2 | highlighter: rouge 3 | markdown: kramdown 4 | permalink: date 5 | maruku: 6 | use_tex: false 7 | use_divs: false 8 | png_engine: blahtex 9 | png_dir: images/latex 10 | png_url: /images/latex 11 | 12 | plugins: 13 | - jekyll-sitemap 14 | 15 | defaults: 16 | - 17 | scope: 18 | path: "" 19 | values: 20 | sitemap: false 21 | 22 | # Excludes should be appended to the default 23 | # https://github.com/jekyll/jekyll/blob/master/lib/site_template/_config.yml#L37-L55 24 | exclude: 25 | - .sass-cache/ 26 | - .jekyll-cache/ 27 | - gemfiles/ 28 | - Gemfile 29 | - Gemfile.lock 30 | - node_modules/ 31 | - vendor/bundle/ 32 | - vendor/cache/ 33 | - vendor/gems/ 34 | - vendor/ruby/ 35 | - CNAME 36 | - CONTRIBUTING.md 37 | - LICENSE 38 | - README.md 39 | - pages/example.md 40 | 41 | future: true 42 | -------------------------------------------------------------------------------- /_includes/welcome.md: -------------------------------------------------------------------------------- 1 | # ようこそ 2 | 3 | 時代遅れの情報がウェブ上にあふれている。そんな情報を見たPHP初心者は戸惑ってしまうだろう。そして、まずい手法やまずいコードが広まってしまう。 4 | そんなのはもうやめよう。_PHP: The Right Way_ は気軽に読めるクイックリファレンスだ。PHPの一般的なコーディング規約、 5 | ウェブ上のよくできたチュートリアルへのリンク、そして現時点でのベストプラクティスだと執筆者が考えていることをまとめた。 6 | 7 | 大事なのは、 8 | _PHPを使うための正式なお作法など存在しない_ ってこと。 9 | このサイトの狙いは、はじめて PHP を使うことになった開発者に、いろんなトピックを紹介すること。 10 | 経験豊富なプロの人にとっても、これまで深く考えることなく使ってきた内容について、新鮮な見方を伝えられるだろう。 11 | このサイトは、決して「どのツールを使えばいいのか」を教えるものじゃない。 12 | いくつかの選択肢を示して、それぞれの違いや使い道をできる限り紹介する。 13 | 14 | このサイトは今も成長中なので、有用な情報やサンプルなどが見つかればどんどん更新していくつもりだ。 15 | 16 | ## 翻訳 17 | 18 | _PHP: The Right Way_ は、他の言語にも翻訳されている。 19 | 20 | * [English](https://www.phptherightway.com) 21 | * [Deutsch](https://rwetzlmayr.github.io/php-the-right-way) 22 | * [Español](https://phpdevenezuela.github.io/php-the-right-way) 23 | * [Français](https://eilgin.github.io/php-the-right-way/) 24 | * [Indonesia](https://id.phptherightway.com) 25 | * [Italiano](https://it.phptherightway.com) 26 | * [Polski](https://pl.phptherightway.com) 27 | * [Português do Brasil](https://br.phptherightway.com) 28 | * [Română](https://bgui.github.io/php-the-right-way/) 29 | * [Slovenščina](https://sl.phptherightway.com) 30 | * [Srpski](https://phpsrbija.github.io/php-the-right-way/) 31 | * [Türkçe](https://hkulekci.github.io/php-the-right-way/) 32 | * [български](https://bg.phptherightway.com) 33 | * [Русский язык](https://getjump.github.io/ru-php-the-right-way) 34 | * [Українська](https://iflista.github.io/php-the-right-way/) 35 | * [العربية](https://adaroobi.github.io/php-the-right-way/) 36 | * [فارسى](https://novid.github.io/php-the-right-way/) 37 | * [ภาษาไทย](https://apzentral.github.io/php-the-right-way/) 38 | * [한국어판](https://modernpug.github.io/php-the-right-way) 39 | * [日本語](https://ja.phptherightway.com) 40 | * [简体中文](https://laravel-china.github.io/php-the-right-way/) 41 | * [繁體中文](https://laravel-taiwan.github.io/php-the-right-way) 42 | 43 | ## Book 44 | 45 | 最新版の _PHP: The Right Way_ (英語版)はPDFやEPUBやMOBIでも公開されている。 [Leanpubでゲットしよう。][1] 46 | 47 | ## 協力するには 48 | 49 | みんなでこのサイトをもっとよいものにしよう。PHPを勉強したい人たちのために良質な情報を集めるんだ! [GitHubでね。][2] 50 | 51 | [1]: https://leanpub.com/phptherightway 52 | [2]: https://github.com/codeguy/php-the-right-way/tree/gh-pages 53 | -------------------------------------------------------------------------------- /_posts/01-01-01-Getting-Started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: はじめに 3 | anchor: getting_started 4 | --- 5 | 6 | # はじめに {#getting_started_title} 7 | 8 | -------------------------------------------------------------------------------- /_posts/01-02-01-Use-the-Current-Stable-Version.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 最新の安定版 (8.3) を使う 3 | isChild: true 4 | anchor: use_the_current_stable_version 5 | --- 6 | 7 | ## 最新の安定版を使う (8.3) {#use_the_current_stable_version_title} 8 | 9 | とりあえず PHP を試したいっていうのなら、最新の安定版である [PHP 8.3][php-release] を使おう。 10 | PHP 8.x では、PHP 7.x や 5.x 系にはない数々の強力な[新機能](#language_highlights)が追加された。 11 | エンジンが大幅に書き直され、今までのバージョンよりもずっと高速になっている。 12 | PHP 8 は言語のメジャーアップデートであり、たくさんの新機能や最適化が含まれている。 13 | 14 | 最新版をできるだけ早めに試してみるべきだ。 15 | PHP 7.4 は [すでにサポートを終了している。][php-supported] 16 | アップグレードはとても簡単だ。というのも、[過去との互換性を損なうような変更(PHP 8.3)][php-bc-83], [(PHP 8.2)][php-bc-82], [(PHP 8.1)][php-bc-81], [(PHP 8.0)][php-bc-80] がそんなに多くないからだ。 17 | 何かの関数や機能がどのバージョンに含まれているのかを知りたければ、[php.net][php-docs] にあるマニュアルを調べよう。 18 | 19 | [php-release]: https://www.php.net/downloads.php 20 | [php-supported]: https://www.php.net/supported-versions.php 21 | [php-docs]: https://www.php.net/manual/ 22 | [php-bc-80]: https://www.php.net/manual/migration80.incompatible.php 23 | [php-bc-81]: https://www.php.net/manual/migration81.incompatible.php 24 | [php-bc-82]: https://www.php.net/manual/migration82.incompatible.php 25 | [php-bc-83]: https://www.php.net/manual/migration83.incompatible.php 26 | -------------------------------------------------------------------------------- /_posts/01-03-01-Built-in-Web-Server.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ビルトインウェブサーバー 3 | isChild: true 4 | anchor: builtin_web_server 5 | --- 6 | 7 | ## ビルトインウェブサーバー {#builtin_web_server_title} 8 | 9 | PHPの勉強を始めるときに、わざわざ本格的なウェブサーバーをインストールする必要はない。そう、PHP 5.4以降ならね。サーバーを立ち上げたかったら、ターミナルでプロジェクトのルートディレクトリに行って、こんなコマンドを実行するだけでいいんだ。 10 | 11 | {% highlight console %} 12 | > php -S localhost:8000 13 | {% endhighlight %} 14 | 15 | * [コマンドラインで使えるビルトインウェブサーバーについて、もっと調べる][cli-server] 16 | 17 | 18 | [cli-server]: https://www.php.net/features.commandline.webserver 19 | -------------------------------------------------------------------------------- /_posts/01-04-01-Mac-Setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Mac の人は 3 | isChild: true 4 | anchor: mac_setup 5 | --- 6 | 7 | ## macOS の人は {#mac_setup_title} 8 | 9 | macOS には PHP が最初からインストールされているけど、最新の安定版からは微妙に遅れている。 10 | 最新の PHP を macOS にインストールするには、いくつかの方法がある。 11 | 12 | ### Homebrew によるインストール 13 | 14 | [Homebrew] は macOS 用の強力なパッケージ管理ツールで、 15 | PHP やその拡張モジュールも簡単にインストールできる。 16 | Homebrew のコアリポジトリで、PHP 7.4、8.0、8.1、8.2、そして PHP 8.3 用の "formulae" が公開されている。 17 | 18 | 最新版の PHP をインストールするには、こんなコマンドを実行すればいい。 19 | 20 | ``` 21 | brew install php@8.3 22 | ``` 23 | 24 | Homebrew の PHP のバージョンを切り替えるには、環境変数 `PATH` を設定すればいい。 25 | [brew-php-switcher][brew-php-switcher] を使えば、そのへんを自動的にやってくれる。 26 | 27 | 手動でリンクを消したり、追加したりすることで、使いたい PHP のバージョンに手動で切り替えることもできる。 28 | 29 | ``` 30 | brew unlink php 31 | brew link --overwrite php@8.2 32 | ``` 33 | 34 | ``` 35 | brew unlink php 36 | brew link --overwrite php@8.3 37 | ``` 38 | 39 | ### Macports によるインストール 40 | 41 | [MacPorts] プロジェクトはオープンソースのコミュニティによる取り組みで、 42 | macOS 上のオープンソースソフトウェアのコンパイルやインストールそしてアップグレードを簡単にできるようにする仕組みだ。 43 | コマンドラインのソフトからX11やAquaベースのソフトにまで対応している。 44 | 45 | MacPorts はコンパイル済みのバイナリにも対応しているので、関連するライブラリなどを毎回ソースからコンパイルしなおす必要はない。 46 | なので、まだ何もパッケージをインストールしていない状態でも、時間の心配をする必要はない。 47 | 48 | 現時点でインストールできるのは `php54`、`php55`、`php56`、`php70`、`php71`、`php72`、`php73`、`php74`、`php80`、`php81`、`php82`、`php83` のいずれかで、`port install` コマンドを使ってこのようにインストールする。 49 | 50 | sudo port install php74 51 | sudo port install php83 52 | 53 | そして、`select` コマンドを使って、アクティブな PHP を切り替える。 54 | 55 | sudo port select --set php php83 56 | 57 | ### phpbrew によるインストール 58 | 59 | [phpbrew] は、複数のバージョンの PHP をインストールして管理するためのツールだ。 60 | 使っているアプリケーションやプロジェクトによって、PHP のバージョンが異なる場合に特に便利で、 61 | 仮想マシンを用意する必要がなくなる。 62 | 63 | ### Liip のバイナリインストーラーによる PHP のインストール 64 | 65 | [php-osx.liip.ch] は、たった一行で PHP をインストールできる方法だ。バージョン 5.3 から 7.3 に対応している。 66 | Apple がインストールした php のバイナリは上書きせず、まったく別の場所 (/usr/local/php5) にすべてをインストールする。 67 | 68 | ### ソースからのコンパイル 69 | 70 | もうひとつの選択肢がある。 [自分でコンパイル][mac-compile] 71 | することだ。この方法ならインストールする PHP のバージョンを完全にコントロールできる。 72 | この場合は、Xcode あるいはその代用ツール ["Command Line Tools for XCode"] 73 | をインストールする必要がある。これらは、 Apple の Developer Center からダウンロードできる。 74 | 75 | ### 全部入りのインストーラー 76 | 77 | ここまでで説明した方法は PHP そのものに関するものばかりで、それ以外のたとえば [Apache][apache]、[Nginx][nginx]、SQL server などは扱っていない。 78 | [MAMP][mamp-downloads] や [XAMPP][xampp] みたいな「全部入り」のインストーラーを使えば、このあたりのソフトも一緒に入れられるし設定もしてくれる。かんたん。 79 | でも、インストーラーに縛られてしまって自由にいじれなくなるという弱点もある。 80 | 81 | [Homebrew]: https://brew.sh/ 82 | [MacPorts]: https://www.macports.org/install.php 83 | [phpbrew]: https://github.com/phpbrew/phpbrew 84 | [php-osx.liip.ch]: https://web.archive.org/web/20220505163210/https://php-osx.liip.ch/ 85 | [mac-compile]: https://www.php.net/install.macosx.compile 86 | [xcode-gcc-substitution]: https://github.com/kennethreitz/osx-gcc-installer 87 | ["Command Line Tools for XCode"]: https://developer.apple.com/downloads 88 | [apache]: https://httpd.apache.org/ 89 | [nginx]: https://www.nginx.com/ 90 | [mamp-downloads]: https://www.mamp.info/en/downloads/ 91 | [xampp]: https://www.apachefriends.org/ 92 | [brew-php-switcher]: https://github.com/philcook/brew-php-switcher 93 | -------------------------------------------------------------------------------- /_posts/01-05-01-Windows-Setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Windows の人は 3 | isChild: true 4 | anchor: windows_setup 5 | --- 6 | 7 | ## Windows の人は {#windows_setup_title} 8 | 9 | [windows.php.net/download][php-downloads] からバイナリをダウンロードしよう。それを展開したら、 10 | PHPフォルダのルート (php.exe がある場所) に [PATH][windows-path] を通しておくといい。そうすれば、どこからでも PHP を実行できるようになる。 11 | 12 | 学習用にローカルで開発する場合は PHP 5.4 以降のビルトインウェブサーバーを使えばよいので、細かい設定を気にする必要はない。 13 | もしウェブサーバーとかMySQLとかも含めた「全部入り」を使いたければ、[XAMPP][xampp]、[EasyPHP][easyphp]、[OpenServer][openserver]、そして[WAMP][wamp]などがお勧めだ。これらを使えば Windows 用の開発環境を手早く構築できる。 14 | 15 | とはいうものの、これらのツールは実際の運用環境と微妙に異なる。なので、たとえば「Windows で開発して Linux にデプロイ」 16 | とかいう場合は環境の違いに気をつける必要がある。 17 | 18 | Windows 上でシステムを実運用する場合は、IIS 7 を使うとよい。これが一番安定しており、かつパフォーマンスも優れている。 19 | [phpmanager][phpmanager] (IIS 7 用の GUI プラグイン) を使えば PHP の設定は管理をシンプルにできる。 20 | IIS 7 には FastCGI が組み込まれており、すぐに使える。 21 | 単に PHP をハンドラとして設定するだけでよい。 22 | その値の詳しい情報は、[dedicated area on iis.net][php-iis]に PHP 専用のエリアがある。 23 | 24 | 開発環境と運用環境が違っていると、いざ動かしたときにおかしなバグが発生しがちだ。 25 | Windowsで開発したアプリケーションをLinux(などの非Windows環境)で動かしているのなら、[仮想マシン](/#virtualization_title)を使うべきだ。 26 | 27 | Chris Tankersleyは彼が[WindowsでのPHP開発][windows-tools]において使えるツールについての役に立つ記事を残しておいてくれている。 28 | 29 | [easyphp]: https://www.easyphp.org/ 30 | [phpmanager]: http://phpmanager.codeplex.com/ 31 | [openserver]: https://ospanel.io/ 32 | [wamp]: https://www.wampserver.com/en/ 33 | [php-downloads]: https://windows.php.net/download/ 34 | [php-iis]: https://php.iis.net/ 35 | [windows-path]: https://www.windows-commandline.com/set-path-command-line/ 36 | [windows-tools]: https://ctankersley.com/2016/11/13/developing-on-windows-2016/ 37 | [xampp]: https://www.apachefriends.org/ 38 | -------------------------------------------------------------------------------- /_posts/01-06-01-Common-Directory-Structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 一般的なディレクトリ構造 3 | isChild: true 4 | anchor: common_directory_structure 5 | --- 6 | 7 | ## 一般的なディレクトリ構造 {#common_directory_structure_title} 8 | 9 | webプログラミングを始めようとしている人たちからよく来る質問のひとつは「僕はどこに自分のプログラミングしたファイルを置けばいいんだい?」というものだ。 10 | 何年もの間、この答えは一貫して「 `ドキュメントルート` に置け」というものだった。この答えは完璧ではないが、ドキュメントルートはプロジェクトを始めるにはいい場所だ。 11 | 12 | セキュリティ上の理由から、設定ファイルはサイト訪問者がアクセスできる場所に置いてはいけない。公開スクリプトは公開ディレクトリに置き、非公開の設定やデータは公開ディレクトリの外に置いておくのがいいだろう。 13 | 14 | チームやCMS、フレームワークによってディレクトリ構造はそれぞれの使われ方をするし、基本はそれに従うのがいいだろう。しかし、そういったものなしにプロジェクトをひとりで始める場合は、どのようなファイルシステム構造を使うことを決めるかは困難だろう。 15 | 16 | [Paul M. Jones]はPHP関連の何万ものgithubプロジェクトに共通する作法について素晴らしい調査を行っている。彼は調査に基づいて、ファイルとディレクトリ構造について[Standard PHP Package Skeleton]にまとめている。 17 | ディレクトリ構造は `ドキュメントルート` は `public/` ディレクトリとするべきであり、ユニットテストは `tests/` ディレクトリ、composerでインストールするようなサードパーティのライブラリは `vendor/` ディレクトリとするのがよいだろう。その他のファイルやディレクトリについても、[Standard PHP Package Skeleton]に従うことがプロジェクトのメンバーにとって最も理に叶うだろう。 18 | 19 | [Paul M. Jones]: https://paul-m-jones.com/ 20 | [Standard PHP Package Skeleton]: https://github.com/php-pds/skeleton 21 | [Composer]: /#composer_and_packagist 22 | -------------------------------------------------------------------------------- /_posts/02-01-01-Code-Style-Guide.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: コーディングスタイル 3 | anchor: code_style_guide 4 | --- 5 | 6 | # コーディングスタイル {#code_style_guide_title} 7 | 8 | PHP のコミュニティはとてもでっかくて、いろんな人たちがいる。 9 | そして、数え切れないほどのライブラリやフレームワークそしてコンポーネントが存在する。 10 | そんな中からいくつか選んで、それを組み合わせてひとつのプロジェクトで使うっていうのもよくあることだ。 11 | 大切なのは、PHP のコードを書くときに、(できるだけ) 標準的なスタイルに従うことだ。 12 | そうすれば、いろんなライブラリを組み合わせて使うのも簡単になる。 13 | 14 | [Framework Interop Group][fig] っていうところ 15 | が、おすすめのスタイルを提案している。 16 | コーディングスタイルとは関係ないものもあるけれど、[PSR-1][psr1]、[PSR-12][psr12]、[PSR-4][psr4]、そして [PER Coding Style][per-cs] はコーディングスタイルを扱っている。 17 | これって要するに、 18 | Drupal や Zend、Symfony、Laravel、CakePHP、phpBB、AWS SDK、FuelPHP、Lithium 19 | などのプロジェクトが採用しつつある規約をまとめただけのものなんだ。 20 | 自分のプロジェクトでこれを使ってもいいし、今までの自分のスタイルを使い続けてもいい。 21 | 22 | 理想を言えば、PHP のコードを書くときには、よく知られた何らかの標準規約に従うべきだ。 23 | さっき説明したPSRの組み合わせでもいいし、PEARとかZendのやつでもかまわない。 24 | そうすれば、他の人にもコードを読んでもらいやすくなるし、手助けも得やすくなるだろう。 25 | また、コンポーネントを実装するアプリケーションがいろんなサードパーティのコードを組み合わせても、一貫性を保てる。 26 | 27 | * [PSR-1 とは][psr1] 28 | * [PSR-12 とは][psr12] 29 | * [PSR-4 とは][psr4] 30 | * [PER Coding Style とは][per-cs] 31 | * [PEARのコーディング規約][pear-cs] 32 | * [Symfonyのコーディング規約][symfony-cs] 33 | 34 | [PHP_CodeSniffer][phpcs]を使えば、 35 | 自分のコードがこれらの標準のどれかひとつに準拠しているかどうかを確認できる。 36 | あと、[Sublime Text][st-cs]みたいなテキストエディタのプラグインを使えば、 37 | 書いているその場でリアルタイムのフィードバックが得られる。 38 | 39 | コードのレイアウトを自動的に修正するツールとしては、二つの選択肢がある。 40 | 41 | - [PHP Coding Standards Fixer][phpcsfixer]がそのひとつで、これはきちんとテストされているコードだ。 42 | - もうひとつの選択肢は [PHP Code Beautifier and Fixer][phpcbf] で、これはPHP_CodeSnifferに含まれている。これを使って、コードのレイアウトを適切に調整できる。 43 | 44 | phpcs をシェルから手動で実行することもできる。 45 | 46 | phpcs -sw --standard=PSR1 file.php 47 | 48 | これは、エラーの内容とその修正方法を表示してくれる。 49 | このコマンドをgit hookに仕込んでおけば便利だろう。 50 | そうすれば、標準規約に反する変更を含むブランチは、それを修正するまでリポジトリに投入できなくなる。 51 | 52 | PHP_CodeSnifferを使っている場合は、指摘されたコードレイアウトの問題を自動的に修正することもできる。そのためには[PHP Code Beautifier and Fixer][phpcbf]を使えばいい。 53 | 54 | phpcbf -w --standard=PSR1 file.php 55 | 56 | もうひとつの選択肢は[PHP Coding Standards Fixer][phpcsfixer]で、 57 | これは、実際に修正するまえにコードにどんな問題があったのかを表示してくれる。 58 | 59 | php-cs-fixer fix -v --rules=@PSR1 file.php 60 | 61 | 変数や関数などのシンボル名、そしてディレクトリ名などのコード基盤なんかは、英語にしておくことをおすすめする。 62 | コードのコメントに関しては、別に英語にこだわらなくてもかまわない。 63 | そのコードを扱う(将来扱う可能性がある)すべての人が読みやすいものであれば、何語でもかまわない。 64 | 65 | PHPでクリーンなコードを書くための資料としておすすめなのが [Clean Code PHP][cleancode] だ。 66 | 67 | [fig]: https://www.php-fig.org/ 68 | [psr1]: https://www.php-fig.org/psr/psr-1/ 69 | [psr12]: https://www.php-fig.org/psr/psr-12/ 70 | [psr4]: https://www.php-fig.org/psr/psr-4/ 71 | [per-cs]: https://www.php-fig.org/per/coding-style/ 72 | [pear-cs]: https://pear.php.net/manual/en/standards.php 73 | [symfony-cs]: https://symfony.com/doc/current/contributing/code/standards.html 74 | [phpcs]: https://github.com/squizlabs/PHP_CodeSniffer 75 | [phpcbf]: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Fixing-Errors-Automatically 76 | [st-cs]: https://github.com/benmatselby/sublime-phpcs 77 | [phpcsfixer]: https://cs.symfony.com/ 78 | [cleancode]: https://github.com/jupeter/clean-code-php 79 | -------------------------------------------------------------------------------- /_posts/03-01-01-Language-Highlights.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 言語仕様のポイント 3 | anchor: language_highlights 4 | --- 5 | 6 | # 言語仕様のポイント {#language_highlights_title} 7 | -------------------------------------------------------------------------------- /_posts/03-02-01-Programming-Paradigms.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: プログラミングのパラダイム 3 | isChild: true 4 | anchor: programming_paradigms 5 | --- 6 | 7 | ## プログラミングのパラダイム {#programming_paradigms_title} 8 | 9 | PHP は柔軟性のある動的言語で、いろんなプログラミングテクニックに対応している。 10 | 長年の間に劇的に成長してきた。PHP 5.0 でのオブジェクト指向モデルの追加 (2004 年)、 11 | PHP 5.3 での無名関数や名前空間の追加 (2009 年)、そして 12 | PHP 5.4 でのトレイトの追加 (2012 年) などが特筆すべきところだろう。 13 | 14 | ### オブジェクト指向プログラミング 15 | 16 | PHP には完全なオブジェクト指向プログラミングの機能が搭載されている。 17 | クラスや抽象クラス、インターフェイス、継承、コンストラクタ、クローン、 18 | 例外などなどといった機能も、当然使える。 19 | 20 | * [PHP のオブジェクト指向][oop] 21 | * [トレイト][traits] 22 | 23 | ### 関数プログラミング 24 | 25 | PHP は、ファーストクラスの関数をサポートしている。 26 | つまり、関数を変数に代入できるってことだ。 27 | 自分で定義した関数だろうがもともと組み込まれている関数だろうが、 28 | 変数で参照したり動的に実行したりできる。 29 | 何かの関数を別の関数の引数として渡すこと ( _高階関数_ っていう機能) 30 | もできるし、関数の返り値を別の関数にすることもできる。 31 | 32 | 再帰 (ある関数の中から自分自身を呼ぶこと) も PHP の機能としてサポートしている。 33 | しかし、たいていの PHP コードはそれよりも逐次処理を重視している。 34 | 35 | 新型の無名関数 (クロージャにも対応したもの) が使えるようになったのは、PHP 5.3 (2009 年) 以降だ。 36 | 37 | PHP 5.4 からは、クロージャをオブジェクトのスコープにバインドできるようになった。 38 | また callable のサポートも強化され、ほとんどの場合で無名関数と互換性を持つようになった。 39 | 40 | * 詳しくは [PHP における関数型プログラミング](/pages/Functional-Programming.html) で 41 | * [無名関数][anonymous-functions] 42 | * [Closure クラス][closure-class] 43 | * [クロージャの詳細を知りたければ、RFCを読めばいいよ][closures-rfc] 44 | * [Callable][callables] 45 | * [`call_user_func_array()`による動的な関数実行][call-user-func-array] 46 | 47 | ### メタプログラミング 48 | 49 | PHP はいろんな形式のメタプログラミングに対応しており、リフレクション API やマジックメソッドが使える。 50 | マジックメソッドには `__get()` や `__set()`、`__clone()`、`__toString()`、そして `__invoke()` 51 | などがあり、これらを活用すればクラスの振る舞いをフックできる。 52 | Ruby の人がよく「PHP には `method_missing` がなくてさあ」とか言うけど、ちゃんと 53 | `__call()` とか `__callStatic()` があるよ。 54 | 55 | * [マジックメソッド][magic-methods] 56 | * [リフレクション][reflection] 57 | * [オーバーロード][overloading] 58 | 59 | 60 | [oop]: https://www.php.net/language.oop5 61 | [traits]: https://www.php.net/language.oop5.traits 62 | [anonymous-functions]: https://www.php.net/functions.anonymous 63 | [closure-class]: https://www.php.net/class.closure 64 | [closures-rfc]: https://wiki.php.net/rfc/closures 65 | [callables]: https://www.php.net/language.types.callable 66 | [call-user-func-array]: https://www.php.net/function.call-user-func-array 67 | [magic-methods]: https://www.php.net/language.oop5.magic 68 | [reflection]: https://www.php.net/intro.reflection 69 | [overloading]: https://www.php.net/language.oop5.overloading 70 | -------------------------------------------------------------------------------- /_posts/03-03-01-Namespaces.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 名前空間 3 | isChild: true 4 | anchor: namespaces 5 | --- 6 | 7 | ## 名前空間 {#namespaces_title} 8 | 9 | さっきも言ったとおり、PHP のコミュニティでは多くの人たちがいろんなコードを書いている。 10 | ってことは、誰かが書いたライブラリと別の人が書いたライブラリでクラス名がダブってしまう可能性があるということだ。 11 | 両者が同じ名前空間を使っていたら、衝突して問題の原因となってしまう。 12 | 13 | そんな問題を解決するのが _名前空間_ だ。 14 | マニュアルにあるとおり、名前空間っていうのは OS のファイルシステムのディレクトリと似てる。 15 | 同じ名前のふたつのファイルを同一ディレクトリに置くことはできないけど、 16 | 別々のディレクトリに分ければ共存できるというわけだ。 17 | 同様に、別々の名前空間に分ければ同じ名前のクラスがふたつあっても共存できる。簡単に言うと、そういうこと。 18 | 19 | 自分の書くコードにも、名前空間を指定することが大切だ。 20 | そうすれば、誰か他の人があなたのコードを使うときに「これ、他のライブラリと競合しないかな」 21 | と悩まずに済む。 22 | 23 | 名前空間のおすすめの使い方が [PSR-4][psr4] にまとまっている。 24 | この文書の狙いは、ファイルやクラスそして名前空間の命名規則を標準化して 25 | お互いに再利用しやすくすることだ。 26 | 27 | 2014年10月、PHP-FIG はオートローディングに関する標準であった [PSR-0][psr0] を非推奨とした。 28 | PSR-0もPSR-4も、今はどちらでも使える。 29 | というのも PSR-4 は PHP 5.3 以降が必須だけれど、今でも PHP 5.2 のプロジェクトはたくさんあって、それらが PSR-0 に従っているからだ。 30 | 31 | これから新しくアプリケーションやパッケージを作るときにオートローダーの使いかたの参考にするのなら、まず間違いなく PSR-4 だ。 32 | 33 | * [名前空間][namespaces] 34 | * [PSR-0][psr0] 35 | * [PSR-4][psr4] 36 | 37 | 38 | [namespaces]: https://www.php.net/language.namespaces 39 | [psr0]: https://www.php-fig.org/psr/psr-0/ 40 | [psr4]: https://www.php-fig.org/psr/psr-4/ 41 | -------------------------------------------------------------------------------- /_posts/03-04-01-Standard-PHP-Library.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Standard PHP Library 3 | isChild: true 4 | anchor: standard_php_library 5 | --- 6 | 7 | ## Standard PHP Library {#standard_php_library_title} 8 | 9 | Standard PHP Library (SPL) とは PHP に組み込まれた標準ライブラリで、 10 | さまざまなクラスやインターフェイスを提供する。 11 | よく使うデータ構造 (スタックやキュー、ヒープなど) 12 | やイテレータなどが含まれており、SPL のインターフェイスを実装したクラスをつくれば 13 | それをイテレータで反復処理されることもできる。 14 | 15 | * [SPL][spl] 16 | * [LinkedIn.comのSPL動画コース(有償)][linkedin] 17 | 18 | 19 | [spl]: https://www.php.net/book.spl 20 | [linkedin]: https://www.linkedin.com/learning/learning-the-standard-php-library?trk=lynda_redirect_learning 21 | -------------------------------------------------------------------------------- /_posts/03-05-01-Command-Line-Interface.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: コマンドラインインターフェイス 3 | isChild: true 4 | anchor: command_line_interface 5 | --- 6 | 7 | ## コマンドラインインターフェイス {#command_line_interface_title} 8 | 9 | PHP はもともとウェブアプリケーションを書くために作られたものだが、 10 | コマンドラインインターフェイス (CLI) のプログラムを書くのにも便利だ。 11 | コマンドラインのプログラムを書けば、テストやデプロイといった 12 | よくある作業を自動化する助けとなる。 13 | 14 | CLI の PHP プログラムが便利なのは、アプリケーションのコードを使うときに 15 | わざわざウェブの UI を用意せずに済むところだ。 16 | ただ、CLI の PHP スクリプトをウェブサーバーの公開ディレクトリに置くことは **絶対禁止** ! 17 | 18 | PHP をコマンドラインで実行してみよう。 19 | 20 | {% highlight console %} 21 | > php -i 22 | {% endhighlight %} 23 | 24 | `-i` は、PHP の設定情報を [`phpinfo`][phpinfo] 関数みたいに表示するオプションだ。 25 | 26 | `-a` オプションで対話シェルを使えるようになる。ruby の IRB とか、Python の対話シェルと同じようなものだ。 27 | それ以外にも、いろんな [コマンドラインオプション][cli-options] がある。 28 | 29 | じゃあ、シンプルな "Hello, $name" プログラムを書いてみよう。`hello.php` というファイルを作って、 30 | こんな内容にする。 31 | 32 | {% highlight php %} 33 | " . PHP_EOL; 36 | exit(1); 37 | } 38 | $name = $argv[1]; 39 | echo "Hello, $name" . PHP_EOL; 40 | {% endhighlight %} 41 | 42 | PHP のスクリプトを実行すると、コマンドラインの引数に関する変数がふたつ設定される。 43 | [`$argc`][argc] は整数値で、引数の *数* を表し、 44 | [`$argv`][argv] は配列で、各引数の *値* を含む。 45 | 最初の引数は、常に PHP スクリプトのファイル名となる。今回の場合なら `hello.php` だ。 46 | 47 | `exit()` でゼロ以外の数値を返すと、コマンドが失敗したことをシェルに伝えることができる。 48 | よく使われる終了コードは [ここ][exit-codes] で調べよう。 49 | 50 | このスクリプトをコマンドラインから実行すると、次のようになる。 51 | 52 | {% highlight console %} 53 | > php hello.php 54 | Usage: php hello.php 55 | > php hello.php world 56 | Hello, world 57 | {% endhighlight %} 58 | 59 | 60 | * [PHP をコマンドラインから実行する方法][php-cli] 61 | 62 | [phpinfo]: https://www.php.net/function.phpinfo 63 | [cli-options]: https://www.php.net/features.commandline.options 64 | [argc]: https://www.php.net/reserved.variables.argc 65 | [argv]: https://www.php.net/reserved.variables.argv 66 | [exit-codes]: https://www.gsp.com/cgi-bin/man.cgi?section=3&topic=sysexits 67 | [php-cli]: https://www.php.net/manual/en/features.commandline.php 68 | -------------------------------------------------------------------------------- /_posts/03-06-01-XDebug.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Xdebug 3 | isChild: true 4 | anchor: xdebug 5 | --- 6 | 7 | ## Xdebug {#xdebug_title} 8 | 9 | ソフトウェア開発におけるいちばん便利なツールといえば、よくできたデバッガだ。 10 | こいつがあれば、コードを実行しながらその状態を追いかけて、スタックの中身を監視したりできる。 11 | XdebugはPHP用のデバッガで、さまざまなIDEに組み込んで使える。 12 | ブレークポイントを設定したりスタックの中身を見たりできるんだ。 13 | さらに、PHPUnitやKCacheGrindといったツールと組み合わせれば、 14 | コードカバレッジ解析やプロファイリングもできる。 15 | 16 | 「実はいま困ってるんだよ。仕方なしに`var_dump()`とか`print_r()`に頼ってるけど、もっといいやりかたがないかなあ…」 17 | そんな君に必要なのは、たぶんデバッガだ。 18 | 19 | [Xdebugのインストール][xdebug-install]はちょっと面倒だけど、 20 | 重要な機能として「リモートデバッグ」が使えるようになる。 21 | ふだんはローカルで開発しているけど、テストはVMや別のサーバーでしているという人は、 22 | 今すぐにでも使えるようにしたい機能だろう。 23 | 24 | 慣例にしたがって、ApacheのVHostあるいは.htaccessファイルにこんな値を設定する。 25 | 26 | {% highlight ini %} 27 | php_value xdebug.remote_host 192.168.?.? 28 | php_value xdebug.remote_port 9000 29 | {% endhighlight %} 30 | 31 | "remote host"と"remote port"は、それぞれ開発に使っているホストとIDEがリスンしているポートに対応する。 32 | あとはIDEの設定で「接続をリスンする」モードに変えて、こんなURLを読み込むだけだ。 33 | 34 | http://your-website.example.com/index.php?XDEBUG_SESSION_START=1 35 | 36 | これで、IDEがスクリプトの実行に割り込んで、ブレークポイントを設定したり 37 | メモリの中身を調べたりできるようになる。 38 | 39 | グラフィカルなデバッガを使えば、コードをステップ実行したり変数の中身を調べたり、 40 | 今の実行環境上でコードを評価したりといったことが簡単にできるようになる。 41 | たいていのIDEには、Xdebugを使ったグラフィカルなデバッグの仕組みが初めから組み込まれているか、 42 | あるいはプラグインが用意されている。 43 | MacGDBpというソフトもある。これは、Xdebugを使うためのフリーでオープンソースなmacOS用GUI環境で、スタンドアロンで使える。 44 | 45 | * [Xdebugについてさらに調べる][xdebug-docs] 46 | * [MacGDBpについてさらに調べる][macgdbp-install] 47 | 48 | 49 | [xdebug-install]: https://xdebug.org/docs/install 50 | [xdebug-docs]: https://xdebug.org/docs/ 51 | [macgdbp-install]: https://www.bluestatic.org/software/macgdbp/ 52 | -------------------------------------------------------------------------------- /_posts/04-01-01-Dependency-Management.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 依存関係の管理 3 | anchor: dependency_management 4 | --- 5 | 6 | # 依存関係の管理 {#dependency_management_title} 7 | 8 | PHP のライブラリやフレームワークやコンポーネントって、大量に存在する。 9 | きっと、あなたのプロジェクトでも何個か使っているだろう。そういうのを、プロジェクトの依存関係という。 10 | つい最近まで、PHP には依存関係をうまく管理する仕組みがなかった。 11 | 手作業で管理していたところで、オートローダーのことも気にしないといけない。 12 | もううんざりだ。 13 | 14 | 現時点でよく使われているパッケージ管理システムは、[Composer] と [PEAR] である。 15 | Composer は現在 PHP のパッケージマネージャーとしていちばん人気があるものだけれど、かつてはパッケージマネージャーといえば PEAR だった。 16 | PEAR の歴史も知っておいたほうがいい。今でも PEAR を使っているところがあるかもしれないし、 17 | たとえ決して使うことがないとしても、知っておいて損はないだろう。 18 | 19 | [Composer]: /#composer_and_packagist 20 | [PEAR]: /#pear 21 | -------------------------------------------------------------------------------- /_posts/04-02-01-Composer-and-Packagist.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Composer と Packagist 3 | isChild: true 4 | anchor: composer_and_packagist 5 | --- 6 | 7 | ## Composer と Packagist {#composer_and_packagist_title} 8 | 9 | Composerは、PHP 用としておすすめの依存管理ツールだ。プロジェクト内の依存関係を 10 | `composer.json` ファイルに書いてシンプルなコマンドを打ち込めば、 11 | Composer が自動的にそれをダウンロードしてくれるだけでなく、オートロードの設定までしてくれるんだ。 12 | Composer は、node.js の NPM や Ruby の Bundler みたいなものだ。 13 | 14 | Composer に対応したライブラリは既にいろいろ出回っていて、自分のプロジェクトですぐに使える。 15 | そんなパッケージをまとめたのが [Packagist]。これは、Composer 対応の PHP ライブラリをまとめた公式リポジトリである。 16 | 17 | ### Composer のインストール 18 | 19 | composer をダウンロードするいちばん安全な方法は、[公式サイトの指示に従うこと](https://getcomposer.org/download/)。 20 | この方法だと、インストーラが壊れていたり改ざんされていたりしないかを確かめられる。 21 | インストーラは、`composer.phar` のバイナリを _カレントディレクトリ_ にインストールする。 22 | 23 | お勧めは、*グローバルにインストールする* (要するに、`/usr/local/bin` にだけ置く) 方式だ。 24 | そのためには、次のコマンドを実行すればいい。 25 | 26 | {% highlight console %} 27 | mv composer.phar /usr/local/bin/composer 28 | {% endhighlight %} 29 | 30 | **注意:** パーミッションのエラーでこのコマンドが失敗する場合は、頭に `sudo` をつけて実行してみよう。 31 | 32 | ローカルにインストールした Composer を実行するときには `php composer.phar` 33 | とする。グローバルにインストールしたのなら、単に `composer` と打つだけだ。 34 | 35 | #### Windows でのインストール 36 | 37 | Windowsの場合、一番簡単なのは [ComposerSetup] インストーラーを使う方法だ。 38 | これは、すべてのユーザーで使えるようにインストールしたうえで `$PATH` も設定してくれるので、 39 | あとはコマンドラインから `composer` を呼ぶだけで使えるようになる。 40 | 41 | ### 依存関係の定義とインストール 42 | 43 | Composer は、プロジェクトの依存関係を `composer.json` というファイルで管理する。 44 | このファイルを手で書き換えてもいいし、Composer を使って編集してもいい。 45 | `composer require` を実行すると、プロジェクトの依存関係を追加する。 46 | もしまだ `composer.json` がなければ、新しいファイルを作る。 47 | この例は、プロジェクトの依存関係に [Twig] を追加するものだ。 48 | 49 | {% highlight console %} 50 | composer require twig/twig:^2.0 51 | {% endhighlight %} 52 | 53 | あるいは、 `composer init` コマンドを実行して、 54 | 自分のプロジェクト用の完全な `composer.json` ファイルを作ることもできる。 55 | どちらの方法にせよ、一度 `composer.json` ファイルを作ってしまえば、 56 | あとは Composer がすべての依存ライブラリをダウンロードして `vendor/` にインストールしてくれる。 57 | 次のコマンドは、すでに `composer.json` ファイルを含むプロジェクトをダウンロードした場合にも使える。 58 | 59 | {% highlight console %} 60 | composer install 61 | {% endhighlight %} 62 | 63 | 次に、アプリケーションで最初に呼ばれる PHP ファイルにこんな行を追加する。 64 | これは、Composer のオートローダーを使ってプロジェクトの依存ライブラリを読むよう指示している。 65 | 66 | {% highlight php %} 67 | pear-channel/package 79 | 80 | "pear"というプレフィックスをハードコードすることで、衝突を回避している。 81 | というのも、pearチャネルの名前と別のパッケージベンダーの名前が同じになってしまう可能性があるからだ。 82 | このようにして、チャネルの短縮名(あるいは完全なURL)を使ってそのパッケージが属するチャネルを指定できるようにする。 83 | 84 | このコードをインストールすると、venderディレクトリの中にチャネル名のディレクトリができあがって、 85 | Composerのオートローダーを通して自動的に使えるようになる。 86 | 87 | > vendor/pear2/pear2-http-request/pear2/HTTP/Request.php 88 | 89 | このPEARパッケージを使うには、単純にこのように参照するだけでいい。 90 | 91 | {% highlight php %} 92 | format('Y-m-d') . PHP_EOL; 25 | {% endhighlight %} 26 | 27 | DateTime を使った計算をするときに使えるのが DateInterval クラスだ。 28 | DateTime には `add()` や `sub()` といったメソッドがあって、その引数に指定するのがこの DateInterval となる。 29 | 1日が86400秒であることを前提としたコードを書いてはいけない。 30 | サマータイムとかタイムゾーンの移動がからむと、この前提はあっさり崩れてしまうからだ。 31 | そんなときには DateInterval を使う。二つの日付の差を計算するときには 32 | `diff()` メソッドを使う。このメソッドは DateInterval を返し、結果を表示するのも簡単だ。 33 | 34 | {% highlight php %} 35 | add(new DateInterval('P1M6D')); 39 | 40 | $diff = $end->diff($start); 41 | echo 'Difference: ' . $diff->format('%m month, %d days (total: %a days)') . PHP_EOL; 42 | // Difference: 1 month, 6 days (total: 37 days) 43 | {% endhighlight %} 44 | 45 | DateTime オブジェクトどうしでごく普通に比較することもできる。 46 | 47 | {% highlight php %} 48 | format('Y-m-d') . ' '; 64 | } 65 | {% endhighlight %} 66 | 67 | PHP APIの拡張として有名なのが [Carbon](http://carbon.nesbot.com) だ。 68 | これは DateTime クラスのすべてを継承しているので、コードの書き換えを最小限に抑えられる。 69 | さらに追加機能として、地域化のサポートや、 70 | DateTime オブジェクトの加減算とフォーマットの方法の追加がある。 71 | また、自分で選んだ日付と時刻をシミュレートする機能もあって、これはコードをテストするときに使える。 72 | 73 | * [DateTime][datetime] 74 | * [日付の書式][dateformat] (日付の書式指定文字列に使えるオプション) 75 | 76 | [datetime]: https://www.php.net/book.datetime 77 | [dateformat]: https://www.php.net/function.date 78 | -------------------------------------------------------------------------------- /_posts/05-04-01-Design-Patterns.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: デザインパターン 3 | isChild: true 4 | anchor: design_patterns 5 | --- 6 | 7 | ## デザインパターン {#design_patterns_title} 8 | 9 | アプリケーションをつくるときには、一般的なパターンに従ってコードを書くとよい。 10 | 同じくプロジェクトの全体構造についても、一般的なパターンに従おう。 11 | なぜそうするといいかというと、自分のコードを管理しやすくなるし、 12 | 何がどうなっているのかを他の開発者にもわかってもらいやすくなるからだ。 13 | 14 | フレームワークを使ってコードを書くと、上位レベルのコードやプロジェクトの構造のほとんどはそのフレームワークの流儀に従うことになる。 15 | すでにそこには、いろんなパターンが適用されているだろう。 16 | ただ、そのフレームワークの上で書く自分のコードの中でどんなパターンを適用するかは、自分次第だ。 17 | 一方、フレームワークを使わずにコードを書く場合はどうだろう。 18 | 自分が書こうとしているアプリケーションのタイプや規模に応じて、最適なパターンをみつける必要がある。 19 | 20 | PHPでのデザインパターンや実装例については、これを見ればいい。 21 | 22 | * 23 | * [https://designpatternsphp.readthedocs.io/](https://designpatternsphp.readthedocs.io/en/latest/) ([PDF download](https://www.computer-pdf.com/web-programming/php/924-tutorial-designpatternsphp-documentation.html)) 24 | -------------------------------------------------------------------------------- /_posts/05-05-01-PHP-and-UTF8.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: UTF-8の扱い 3 | isChild: true 4 | anchor: php_and_utf8 5 | --- 6 | 7 | ## UTF-8の扱い {#php_and_utf8_title} 8 | 9 | _このセクションは、もともと[Alex Cabal](https://alexcabal.com/)が 10 | [PHP Best Practices](https://phpbestpractices.org/#utf-8)向けに書いたものだ。この記事をもとに、UTF-8について説明する。_ 11 | 12 | ### 一発で済ませる方法はない。注意して、きちんと一貫性を保つこと。 13 | 14 | 今のところPHPは、低レベルではUnicodeをサポートしていない。 15 | PHPでUTF-8文字列をきちんと処理する方法もあるにはあるが、簡単ではない。さらに、ウェブアプリケーションのあらゆるレベル 16 | (つまり、HTMLやSQLからPHPまで)に手を入れる必要がある。 17 | ここでは、それらについて、現実的な範囲で手短にまとめた。 18 | 19 | ### PHPレベルでのUTF-8 20 | 21 | 文字列の連結や変数への代入などの基本操作については、UTF-8だからといって何か特別なことをする必要はない。 22 | しかし、大半の文字列関数(`strpos()` や `strlen()` など)については、そういうわけにはいかない。 23 | これらの関数には、対応する関数として `mb_*` が用意されていることが多い。 24 | たとえば `mb_strpos()` や `mb_strlen()` だ。 25 | これらの `mb_*` 関数は [マルチバイト文字列拡張モジュール] が提供するもので、 26 | Unicode文字列を扱えるように設計されている。 27 | 28 | Unicode文字列を扱う場合は、常に `mb_*` 関数を使う必要がある。 29 | たとえば、UTF-8文字列に対して `substr()` を使うと、その結果の中に文字化けした半角文字が含まれてしまう可能性がある。 30 | この場合、マルチバイト文字列のときに使うべき関数は `mb_substr()` だ。 31 | 32 | 常に `mb_*` 関数を使うように覚えておくのが大変なところだ。 33 | たとえ一か所でもそれを忘れてしまうと、それ以降の Unicode 文字列は壊れてしまう可能性がある。 34 | 35 | そして、すべての文字列関数に `mb_*` 版があるわけではない。 36 | 自分が使いたい関数にマルチバイト版がないだって? 37 | ご愁傷様。 38 | 39 | すべてのPHPスクリプトの先頭(あるいは、グローバルにインクルードするファイルの先頭)で `mb_internal_encoding()` 40 | 関数を使わないといけないし、スクリプトの中でブラウザに出力するつもりなら、それだけではなく 41 | `mb_http_output()` 関数も使わなければいけない。 42 | すべてのスクリプトでエンコーディングを明示しておけば、後で悩まされることもなくなるだろう。 43 | 44 | さらに、文字列を操作する関数の多くには、文字エンコーディングを指定するためのオプション引数が用意されている。 45 | このオプションがある場合は、常に UTF-8 を明示しておくべきだ。 46 | たとえば `htmlentities()` には文字エンコーディングを設定する引数があるので、 47 | UTF-8 文字列を扱うなら常にそう指定しておかないといけない。 48 | PHP 5.4.0 以降では、 `htmlentities()` や `htmlspecialchars()` のデフォルトエンコーディングが UTF-8 に変わった。 49 | 50 | 最後に、他の人たち向けに配布するつもりのアプリケーションなど、その実行環境で `mbstring` が使えるかどうか定かではない場合は、 51 | Composer の [symfony/polyfill-mbstring] パッケージを使うことも検討しよう。 52 | これは、もし `mbstring` があればそれを使い、なければ非 UTF-8 関数にフォールバックするというものだ。 53 | 54 | [マルチバイト文字列拡張モジュール]: https://www.php.net/book.mbstring 55 | [symfony/polyfill-mbstring]: https://packagist.org/packages/symfony/polyfill-mbstring 56 | 57 | ### データベースレベルでのUTF-8 58 | 59 | PHP スクリプトから MySQL に接続する場合は、上で書いた注意をすべて守ったにもかかわらず UTF-8 60 | 文字列がそれ以外のエンコーディングで格納されてしまう可能性がある。 61 | 62 | PHP から MySQL に渡す文字列を確実に UTF-8 として扱わせるには、データベースとテーブルの文字セットや照合順序の設定を、すべて 63 | `utf8mb4` にしておく必要がある。さらに、PDO の接続文字列にも、文字セットとして `utf8mb4` を指定する。 64 | 詳細は以下のコードを参照すること。 _これ、試験に出るよ。_ 65 | 66 | UTF-8 を完全にサポートするには、文字セット `utf8mb4` を使わないといけない。 `utf8` はダメ!!! 67 | その理由が知りたければ「あわせて読みたい」を参照すること。 68 | 69 | ### ブラウザレベルでのUTF-8 70 | 71 | `mb_http_output()` 関数を使えば、PHP スクリプトからブラウザへの出力が UTF-8 文字列になることを保証できる。 72 | 73 | あとは、HTTPレスポンスの中で、そのページをUTF-8として扱うようブラウザに指示する必要がある。 74 | いまどきなら、普通はHTTPレスポンスヘッダの中でこんなふうに設定するだろう。 75 | 76 | {% highlight php %} 77 | ` タグの中で [charset の `` タグ](http://htmlpurifier.org/docs/enduser-utf8.html) を指定したりしていたものだ。 82 | 83 | {% highlight php %} 84 | PDO::ERRMODE_EXCEPTION, 111 | PDO::ATTR_PERSISTENT => false 112 | ) 113 | ); 114 | 115 | // 変換した文字列を、UTF-8としてデータベースに格納する。 116 | // DBとテーブルの文字セットや照合順序が、ちゃんとutf8mb4になっているかな? 117 | $handle = $link->prepare('insert into ElvishSentences (Id, Body, Priority) values (default, :body, :priority)'); 118 | $handle->bindParam(':body', $string, PDO::PARAM_STR); 119 | $priority = 45; 120 | $handle->bindParam(':priority', $priority, PDO::PARAM_INT); // intを求めていることをpdoに対して明示する 121 | $handle->execute(); 122 | 123 | // 今格納したばかりの文字列を取り出して、きちんと格納できているかどうかを確かめる 124 | $handle = $link->prepare('select * from ElvishSentences where Id = :id'); 125 | $id = 7; 126 | $handle->bindParam(':id', $id, PDO::PARAM_INT); 127 | $handle->execute(); 128 | 129 | // 結果をオブジェクトに代入して、後でHTMLの中で使う 130 | // このオブジェクトがメモリを圧迫することはない。必要になったその時点ではじめてフェッチする 131 | $result = $handle->fetchAll(\PDO::FETCH_OBJ); 132 | 133 | // ラッパーのサンプル。これはデータをhtml出力用にエスケープする 134 | function escape_to_html($dirty){ 135 | echo htmlspecialchars($dirty, ENT_QUOTES, 'UTF-8'); 136 | } 137 | 138 | header('Content-Type: text/html; charset=UTF-8'); // すでに default_charset が utf-8 になっているのであればこれは不要 139 | ?> 140 | 141 | 142 | 143 | UTF-8 テストページ 144 | 145 | 146 | Body); // 変換したUTF-8文字列が、ブラウザに正しく出力されるはず 149 | } 150 | ?> 151 | 152 | 153 | {% endhighlight %} 154 | 155 | ### あわせて読みたい 156 | 157 | * [PHPマニュアル: 文字列演算子](https://www.php.net/language.operators.string) 158 | * [PHPマニュアル: String関数](https://www.php.net/ref.strings) 159 | * [`strpos()`](https://www.php.net/function.strpos) 160 | * [`strlen()`](https://www.php.net/function.strlen) 161 | * [`substr()`](https://www.php.net/function.substr) 162 | * [PHPマニュアル: マルチバイト文字列関数](https://www.php.net/ref.mbstring) 163 | * [`mb_strpos()`](https://www.php.net/function.mb-strpos) 164 | * [`mb_strlen()`](https://www.php.net/function.mb-strlen) 165 | * [`mb_substr()`](https://www.php.net/function.mb-substr) 166 | * [`mb_internal_encoding()`](https://www.php.net/function.mb-internal-encoding) 167 | * [`mb_http_output()`](https://www.php.net/function.mb-http-output) 168 | * [`htmlentities()`](https://www.php.net/function.htmlentities) 169 | * [`htmlspecialchars()`](https://www.php.net/function.htmlspecialchars) 170 | * [Stack Overflow: What factors make PHP Unicode-incompatible?](https://stackoverflow.com/questions/571694/what-factors-make-php-unicode-incompatible) 171 | * [Stack Overflow: Best practices in PHP and MySQL with international strings](https://stackoverflow.com/questions/140728/best-practices-in-php-and-mysql-with-international-strings) 172 | * [How to support full Unicode in MySQL databases](https://mathiasbynens.be/notes/mysql-utf8mb4) 173 | * [Bringing Unicode to PHP with Portable UTF-8](https://www.sitepoint.com/bringing-unicode-to-php-with-portable-utf8/) 174 | * [Stack Overflow: DOMDocument loadHTML does not encode UTF-8 correctly](https://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly) 175 | -------------------------------------------------------------------------------- /_posts/06-01-01-Dependency-Injection.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 依存性の注入 3 | anchor: dependency_injection 4 | --- 5 | 6 | # 依存性の注入 {#dependency_injection_title} 7 | 8 | [Wikipedia](https://wikipedia.org/wiki/Dependency_injection)によると、 9 | 10 | > 依存性の注入とはソフトウェアデザインパターンの1つである。依存関係をソースコードから排除して、実行時あるいはコンパイル時にその依存関係を変更できるようにする。 11 | 12 | とのことだが、この説明は必要以上に小難しく言っているように思える。 13 | 依存性の注入とはコンポーネントに依存関係を渡せる仕組みのことで、コンストラクタで注入したりメソッド呼び出しをしたりプロパティを設定したりといった方法がある。 14 | それだけのことだ。 15 | -------------------------------------------------------------------------------- /_posts/06-02-01-Basic-Concept.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 基本的な概念 3 | isChild: true 4 | anchor: basic_concept 5 | --- 6 | 7 | ## 基本的な概念 {#basic_concept_title} 8 | 9 | 考えかたを説明するために、シンプルな例を示そう。細かいところは手を抜いているけどね。 10 | 11 | ここに `Database` クラスがある。こいつがデータベースとやりとりするためには、何らかのアダプターが必要だ。 12 | そこで、コンストラクタの中でアダプターのインスタンスを作っている。アダプターの名前を直接指定してね。 13 | こんな書きかただと `Database` クラスを単体でテストするのが難しくなるし、 14 | このクラスがアダプターと密接に結びついてしまう。 15 | 16 | {% highlight php %} 17 | adapter = new MySqlAdapter; 27 | } 28 | } 29 | 30 | class MysqlAdapter {} 31 | {% endhighlight %} 32 | 33 | このコードを書き換えて依存性の注入を使うようにすれば、この依存関係を緩やかにできる。 34 | ここでは、コンストラクタに依存性を注入することにし、クラスを横断してプロパティを使えるようにするために [コンストラクタのプロモーション][php-constructor-promotion] を使うことにしよう: 35 | 36 | {% highlight php %} 37 | query("SELECT some_field FROM some_table"); 18 | $row = $statement->fetch(PDO::FETCH_ASSOC); 19 | echo htmlentities($row['some_field']); 20 | 21 | // PDO + SQLite 22 | $pdo = new PDO('sqlite:/path/db/foo.sqlite'); 23 | $statement = $pdo->query("SELECT some_field FROM some_table"); 24 | $row = $statement->fetch(PDO::FETCH_ASSOC); 25 | echo htmlentities($row['some_field']); 26 | {% endhighlight %} 27 | 28 | PDO は、SQL のクエリをデータベースにあわせて変換するものではないし、 29 | もともと存在しない機能をエミュレートするものでもない。 30 | 純粋に、いろんなデータベースに同じ API で接続するためのものだ。 31 | 32 | もっと大切なことは、`PDO` を使えば、外部からの入力 (ID など) 33 | を安全に SQL クエリに埋め込めるということだ。データベースへの 34 | SQL インジェクション攻撃を心配しなくてもよくなる。 35 | そのためには、PDO ステートメントとバインド変数を使えばよい。 36 | 37 | 数値の ID をクエリ文字列として受け取る PHP スクリプトを考えてみよう。 38 | 渡された ID を使って、データベースからユーザー情報を取り出す。 39 | 最初に示すのは `悪い方法` だ。 40 | 41 | {% highlight php %} 42 | query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- ダメ、ゼッタイ! 45 | {% endhighlight %} 46 | 47 | こんな恐ろしいコードを書いちゃいけない。 48 | これは、クエリ文字列のパラメータを SQL に直に埋め込んでいることになる。 49 | あっという間に [SQLインジェクション] 攻撃を食らうだろう。 50 | どこかのずるがしこい攻撃者が `id` に渡す内容をひと工夫して 51 | `http://domain.com/?id=1%3BDELETE+FROM+users` みたいな URL を呼んだとしよう。 52 | このとき変数 `$_GET['id']` の内容は `1;DELETE FROM users` となり、全ユーザーが消えてしまうことになる! 53 | こんな書き方ではなく、PDO のバインド変数で ID を受け取らないといけない。 54 | 55 | {% highlight php %} 56 | prepare('SELECT name FROM users WHERE id = :id'); 59 | $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- まずデータのフィルタリングを行う ([データのフィルタリング](#data_filtering) を参照)。特に INSERT や UPDATE などで重要 60 | $stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- PDOが自動的にSQLのエスケープ処理をする 61 | $stmt->execute(); 62 | {% endhighlight %} 63 | 64 | これが正しい方法だ。この例では、PDO ステートメントでバインド変数を使っている。 65 | 外部からの入力である ID がエスケープされてからデータベースに渡るので、 66 | SQL インジェクション攻撃を受けることがなくなる。 67 | 68 | INSERT や UPDATE などの書き込み操作の場合は、まず [データをフィルタリング](#data_filtering) することが大切だ。 69 | その後で、その他 (HTML タグや JavaScript など) のエスケープを行う。 70 | PDO はあくまでも SQL 用のエスケープを行うものであり、アプリケーション全体の面倒をみてくれるわけではない。 71 | 72 | * [PDOについて調べる][pdo] 73 | 74 | あと、データベースのコネクションはリソースを使うということにも気をつけよう。 75 | コネクションを明示的に閉じることを忘れたせいでリソースを食いつぶしてしまうなんて話は珍しくない。 76 | とはいえ、これは別にPHPに限った話でもないけどね。 77 | PDOを使っている場合は、オブジェクトへの参照をすべて削除して(Nullを代入するなどして) 78 | オブジェクトを破棄してしまえば、暗黙のうちにコネクションを閉じることが保証される。 79 | オブジェクトを明示的に破棄しない場合は、スクリプトの実行が終わった時点でPHPが自動的に接続を閉じる。 80 | もちろん、持続的接続を使っている場合は別だ。 81 | 82 | * [PDOの接続について調べる] 83 | 84 | 85 | [pdo]: https://www.php.net/pdo 86 | [SQLインジェクション]: https://web.archive.org/web/20210413233627/http://wiki.hashphp.org/Validation 87 | [PDOの接続について調べる]: https://www.php.net/pdo.connections 88 | -------------------------------------------------------------------------------- /_posts/07-04-01-Interacting-via-Code.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: データベースとのやりとり 4 | anchor: databases_interacting 5 | --- 6 | 7 | ## データベースとのやりとり {#databases_interacting_title} 8 | 9 | PHPを勉強し始めたばかりの開発者がやってしまいがちなのが、データベースとのやりとりと画面表示のロジックをごちゃまぜにしてしまうことだ。 10 | たとえば、こんなコードになる。 11 | 12 | {% highlight php %} 13 |
    14 | query('SELECT * FROM table') as $row) { 16 | echo "
  • ".$row['field1']." - ".$row['field1']."
  • "; 17 | } 18 | ?> 19 |
20 | {% endhighlight %} 21 | 22 | これは、あらゆる意味でよろしくない。 23 | まず何と言っても、デバッグしづらいし、テストもしづらいし、読みづらい。 24 | あと、何も制限をかけていない場合に、大量のフィールドを出力してしまうことになる。 25 | 26 | 同じことをもっとすっきり行う方法はいろいろある。[OOP](/#object-oriented-programming)が好きな人向けのやりかたもあれば 27 | [関数型プログラミング](/#functional-programming)が好きな人向けのやりかたもある。 28 | が、まずは、分離することからはじめよう。 29 | 30 | これが第一歩だ。 31 | 32 | {% highlight php %} 33 | query('SELECT * FROM table'); 36 | } 37 | 38 | $results = getAllFoos($db); 39 | foreach ($results as $row) { 40 | echo "
  • ".$row['field1']." - ".$row['field1']."
  • "; // BAD!! 41 | } 42 | {% endhighlight %} 43 | 44 | 少しはマシになった。この二つを別々のファイルに分けてしまえば、きれいに分離できるだろう。 45 | 46 | 次に、このメソッドを保持するクラスを用意する。「モデル」だ。 47 | そして、シンプルな `.php` ファイルをもうひとつ作って、そこに画面表示ロジックを入れる。「ビュー」だ。 48 | これで、何となく [MVC] っぽくなった。これは、多くの [フレームワーク](/#frameworks) で使われている、OOPのアーキテクチャだ。 49 | 50 | **foo.php** 51 | 52 | {% highlight php %} 53 | getAllFoos(); 63 | 64 | // ビューを表示する 65 | include 'views/foo-list.php'; 66 | {% endhighlight %} 67 | 68 | 69 | **models/FooModel.php** 70 | 71 | {% highlight php %} 72 | db->query('SELECT * FROM table'); 81 | } 82 | } 83 | {% endhighlight %} 84 | 85 | **views/foo-list.php** 86 | 87 | {% highlight php %} 88 | 89 |
  • -
  • 90 | 91 | {% endhighlight %} 92 | 93 | 本質的にこれは、今どきのフレームワークがやっていることと同じだ。それを手作業でやってみた。 94 | 毎回こんなことをする必要はないかもしれないが、画面表示とデータベース操作を混在させすぎると、 95 | [ユニットテスト](/#unit-testing) をしたくなったときにやっかいな問題が発生してしまう。 96 | 97 | [MVC]: https://code.tutsplus.com/tutorials/mvc-for-noobs--net-10488 98 | -------------------------------------------------------------------------------- /_posts/07-05-01-Abstraction-Layers.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 抽象化レイヤー 4 | anchor: databases_abstraction_layers 5 | --- 6 | 7 | ## 抽象化レイヤー {#databases_abstraction_layers_title} 8 | 9 | 多くのフレームワークは、データベースの抽象化レイヤーを用意している。[PDO][1]を利用しているものもあれば、そうでないものもある。 10 | あるデータベースには存在するけれども別のデータベースには存在しない機能などをエミュレートするために、 11 | クエリーをPHPのメソッドでラップしたりするものだ。 12 | PDOではデータベースへの接続の抽象化しかしないが、フレームワークの抽象化レイヤーは、それ以上のことをしてくれる。 13 | もちろんそれなりのオーバーヘッドが発生するだろう。 14 | でも、MySQLでもPostgreSQLでもSQLiteでも動くようなアプリケーションを書いているのなら、 15 | 多少のオーバーヘッドを割り引いてでも、すっきりしたコードを書けるほうがうれしいだろう。 16 | 17 | 以下の抽象化レイヤーは、 [PSR-0][psr0] や [PSR-4][psr4] で定められた標準名前空間に従っている。 18 | これらはアプリケーションを問わずに利用できる。 19 | 20 | * [Atlas][5] 21 | * [Aura SQL][6] 22 | * [Doctrine2 DBAL][2] 23 | * [Medoo][8] 24 | * [Propel][7] 25 | * [laminas-db][4] 26 | 27 | 28 | [1]: https://www.php.net/book.pdo 29 | [2]: https://www.doctrine-project.org/projects/dbal.html 30 | [4]: https://docs.laminas.dev/laminas-db/ 31 | [5]: https://atlasphp.io 32 | [6]: https://github.com/auraphp/Aura.Sql 33 | [7]: https://propelorm.org/ 34 | [8]: https://medoo.in/ 35 | [psr0]: https://www.php-fig.org/psr/psr-0/ 36 | [psr4]: https://www.php-fig.org/psr/psr-4/ 37 | -------------------------------------------------------------------------------- /_posts/08-01-01-Templating.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: テンプレート 3 | anchor: templating 4 | --- 5 | 6 | # テンプレート {#templating_title} 7 | 8 | テンプレートは、コントローラーやドメインロジックを、プレゼンテーションロジックから切り離すための便利な手段だ。 9 | テンプレ—トには、アプリケーションで使うHTMLを含めることが多いが、それ以外のフォーマット(XMLなど)を含めることもある。 10 | テンプレートのことを「ビュー」と呼ぶこともある。いわゆる 11 | [model–view–controller](/pages/Design-Patterns.html#model-view-controller) (MVC) 12 | パターンの、二番目の要素 **の一部** だ。 13 | -------------------------------------------------------------------------------- /_posts/08-02-01-Benefits.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: メリット 4 | anchor: templating_benefits 5 | --- 6 | 7 | ## メリット {#templating_benefits_title} 8 | 9 | テンプレートを使う主なメリットは、画面に表示する内容をアプリケーションから切り離せることだ。 10 | テンプレートは、フォーマット済みのコンテンツを表示するという責務だけを負う。 11 | データを検索したり、データベースに保存したりなどといった、雑多なタスクは気にしない。 12 | このおかげで、すっきりしたリーダブルなコードが書けるようになる。チームで開発する際などには、これが特に有用だ。 13 | 開発者はサーバー側のコード(コントローラやモデル)、そしてデザイナーはクライアント側のコード(マークアップ) 14 | などという作業分担をしやすくなる。 15 | 16 | テンプレートには、プレゼンテーションのコードの構造を改善するという効果もある。 17 | テンプレートは一般的に「views」などのフォルダにまとめられて、それぞれ個別のファイルになっていることが多い。 18 | こうしておけばコードの再利用がしやすくなる。大規模なコードブロックを、小さめの再利用しやすいパーツ(パーシャルと呼ばれることもある)に分割できるからだ。 19 | たとえば、サイトのヘッダやフッタをテンプレートにしておけば、各ページのテンプレートにそれをインクルードできるようになる。 20 | 21 | 利用するライブラリによっては、テンプレートを使うことで、よりセキュリティを確保できるようにもなる。 22 | ユーザーが作るコンテンツを自動的にエスケープしてくれたりする機能を持つようなテンプレートが、それにあたる。 23 | さらに、サンドボックス機能を提供するライブラリもある。これは、デザイナーが、あらかじめ許可された変数と関数しか利用できないようにする仕組みだ。 24 | -------------------------------------------------------------------------------- /_posts/08-03-01-Plain-PHP-Templates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Plain PHP Templates 3 | isChild: true 4 | title: プレーンなPHPによるテンプレート 5 | anchor: plain_php_templates 6 | --- 7 | 8 | ## プレーンなPHPによるテンプレート {#plain_php_templates_title} 9 | 10 | プレーンなPHPによるテンプレートとは、単にPHPのコードを使ったテンプレートという意味だ。 11 | ごく自然な選択肢だとも言える。そもそもPHP自体がテンプレート言語だし。 12 | あ、これって単に、PHPのコードをHTMLとかにも埋め込めるよねっていう以上の深い意味はないからね。 13 | PHPの開発者にとっては、新しい構文を覚えずに済むというメリットがある。 14 | どんな関数が使えるのかもわかっているし、ふだんPHPを書いているエディタのシンタックスハイライトや 15 | 自動補完機能も、そのまま使える。 16 | その上、プレーンなPHPのテンプレートは高速であることが多い。コンパイルが不要だからだ。 17 | 18 | いまどきのPHPフレームワークは、たいてい何らかの仕組みのテンプレートシステムを持っている。 19 | その多くでデフォルトになっているのが、プレーンPHPによるテンプレートだ。 20 | フレームワーク以外では、[Plates][plates]や[Aura.View][aura] 21 | といったライブラリがプレーンPHPテンプレートを使いやすくしてくれる。継承やレイアウト、拡張などの便利なテンプレート機能を用意してくれるんだ。 22 | 23 | ### プレーンPHPテンプレートのシンプルな例 24 | 25 | [Plates][plates] ライブラリを使った。 26 | 27 | {% highlight php %} 28 | 29 | 30 | insert('header', ['title' => 'User Profile']) ?> 31 | 32 |

    User Profile

    33 |

    Hello, escape($name)?>

    34 | 35 | insert('footer') ?> 36 | {% endhighlight %} 37 | 38 | ### プレーンPHPテンプレートで継承を使う例 39 | 40 | [Plates][plates] ライブラリを使った。 41 | 42 | {% highlight php %} 43 | 44 | 45 | 46 | 47 | <?=$title?> 48 | 49 | 50 | 51 |
    52 | section('content')?> 53 |
    54 | 55 | 56 | 57 | {% endhighlight %} 58 | 59 | {% highlight php %} 60 | 61 | 62 | layout('template', ['title' => 'User Profile']) ?> 63 | 64 |

    User Profile

    65 |

    Hello, escape($name)?>

    66 | {% endhighlight %} 67 | 68 | 69 | [plates]: https://platesphp.com/ 70 | [aura]: https://github.com/auraphp/Aura.View 71 | -------------------------------------------------------------------------------- /_posts/08-04-01-Compiled-Templates.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: コンパイル形式のテンプレート 4 | anchor: compiled_templates 5 | --- 6 | 7 | ## コンパイル形式のテンプレート {#compiled_templates_title} 8 | 9 | PHPはオブジェクト指向言語として成熟してきてはいるものの、テンプレート言語としては 10 | [いまいち][article_templating_engines] だ。 11 | コンパイル形式のテンプレート、たとえば [Twig] や [Brainy] や [Smarty]* 12 | が、この穴を埋めてくれる。テンプレートに特化した、新しい構文を用意してくれるんだ。 13 | 自動エスケープから継承や制御構文まで、コンパイル形式のテンプレートは、いかに読み書きしやすく、安心して使えるかを重視して作られている。 14 | さらに、コンパイル形式のテンプレートは、別の言語でさえも使うことができる。[Mustache] がそのよい例だ。 15 | テンプレートをコンパイルする時間がかかるので、多少はパフォーマンスに影響する。 16 | しかし、適切にキャッシュをすれば、その影響は微々たるものだ。 17 | 18 | **Smartyには自動エスケープ機能があるけど、これはデフォルトでは無効になっている。* 19 | 20 | ### コンパイル形式のテンプレートのシンプルな例 21 | 22 | [Twig] ライブラリを使った。 23 | 24 | {% highlight html+jinja %} 25 | {% raw %} 26 | {% include 'header.html' with {'title': 'User Profile'} %} 27 | 28 |

    User Profile

    29 |

    Hello, {{ name }}

    30 | 31 | {% include 'footer.html' %} 32 | {% endraw %} 33 | {% endhighlight %} 34 | 35 | ### コンパイル形式のテンプレートで継承を使う例 36 | 37 | [Twig] ライブラリを使った。 38 | 39 | {% highlight html+jinja %} 40 | {% raw %} 41 | // template.html 42 | 43 | 44 | 45 | {% block title %}{% endblock %} 46 | 47 | 48 | 49 |
    50 | {% block content %}{% endblock %} 51 |
    52 | 53 | 54 | 55 | {% endraw %} 56 | {% endhighlight %} 57 | 58 | {% highlight html+jinja %} 59 | {% raw %} 60 | // user_profile.html 61 | 62 | {% extends "template.html" %} 63 | 64 | {% block title %}User Profile{% endblock %} 65 | {% block content %} 66 |

    User Profile

    67 |

    Hello, {{ name }}

    68 | {% endblock %} 69 | {% endraw %} 70 | {% endhighlight %} 71 | 72 | 73 | [article_templating_engines]: http://fabien.potencier.org/templating-engines-in-php.html 74 | [Twig]: https://twig.symfony.com/ 75 | [Brainy]: https://github.com/box/brainy 76 | [Smarty]: https://www.smarty.net/ 77 | [Mustache]: https://mustache.github.io/ 78 | -------------------------------------------------------------------------------- /_posts/08-05-01-Further-Reading.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: あわせて読みたい 4 | anchor: templating_further_reading 5 | --- 6 | 7 | ## あわせて読みたい {#templating_further_reading_title} 8 | 9 | ### 記事やチュートリアル 10 | 11 | * [Templating Engines in PHP](http://fabien.potencier.org/templating-engines-in-php.html) 12 | * [An Introduction to Views & Templating in CodeIgniter](https://code.tutsplus.com/tutorials/an-introduction-to-views-templating-in-codeigniter--net-25648) 13 | * [Getting Started With PHP Templating](https://www.smashingmagazine.com/2011/10/getting-started-with-php-templating/) 14 | * [Roll Your Own Templating System in PHP](https://code.tutsplus.com/tutorials/roll-your-own-templating-system-in-php--net-16596) 15 | * [Master Pages](https://laracasts.com/series/laravel-from-scratch/episodes/7) 16 | * [Working With Templates in Symfony 2](https://code.tutsplus.com/tutorials/working-with-templates-in-symfony-2--cms-21172) 17 | * [Writing Safer Templates](https://github.com/box/brainy/wiki/Writing-Safe-Templates) 18 | 19 | ### ライブラリ 20 | 21 | * [Aura.View](https://github.com/auraphp/Aura.View) *(native)* 22 | * [Blade](https://laravel.com/docs/blade) *(compiled, framework specific)* 23 | * [Brainy](https://github.com/box/brainy) *(compiled)* 24 | * [Latte](https://github.com/nette/latte) *(compiled)* 25 | * [Mustache](https://github.com/bobthecow/mustache.php) *(compiled)* 26 | * [PHPTAL](https://phptal.org/) *(compiled)* 27 | * [Plates](https://platesphp.com/) *(native)* 28 | * [Smarty](https://www.smarty.net/) *(compiled)* 29 | * [Twig](https://twig.symfony.com/) *(compiled)* 30 | * [laminas-view](https://docs.laminas.dev/laminas-view/) *(native, framework specific)* 31 | -------------------------------------------------------------------------------- /_posts/09-01-01-Errors-and-Exceptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: エラーと例外処理 3 | anchor: errors_and_exceptions 4 | --- 5 | 6 | # エラーと例外処理 {#errors_and_exceptions_title} 7 | 8 | -------------------------------------------------------------------------------- /_posts/09-02-01-Errors.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: エラー 3 | isChild: true 4 | anchor: errors 5 | --- 6 | 7 | ## エラー {#errors_title} 8 | 9 | 例外処理を重視するプログラミング言語では、何か問題が起こったらすぐに例外を投げる。 10 | それはそれでいいことではあるが、PHPはそうではなく、「例外処理も使える」プログラミング言語だ。 11 | PHPには例外処理の仕組みがあるし、コアの中でもオブジェクトを扱うときには例外処理を行うことが増えている。 12 | でも、PHPは基本的に、よっぽど致命的なエラーが発生しない限りは何があろうと処理を続行しようとする。 13 | 14 | たとえば、こんなコードを考えてみよう。 15 | 16 | {% highlight console %} 17 | $ php -a 18 | php > echo $foo; 19 | Notice: Undefined variable: foo in php shell code on line 1 20 | {% endhighlight %} 21 | 22 | 単純にnoticeレベルのエラーになるだけで、PHPはそのまま処理を続行する。 23 | 例外処理を重視する世界からやってきた人にとっては、これは少しキモいと思うかもしれないね。 24 | たとえばPythonなら、未定義の変数を参照しようとすると、例外が発生する。 25 | 26 | {% highlight console %} 27 | $ python 28 | >>> print foo 29 | Traceback (most recent call last): 30 | File "", line 1, in 31 | NameError: name 'foo' is not defined 32 | {% endhighlight %} 33 | 34 | 実際の違いは、こういうことだ。 35 | Pythonは、些細なことにまでこだわることで、プログラマーが余計な心配(「もし○○だったら、その場合は…」と考えたり、エッジケースを考慮したりなど) 36 | をせずに済むようにしている。 37 | 一方PHPは、どうしようもないエラーが発生しないかぎりは、一応エラーを報告したうえで処理を続行する。 38 | 39 | ### エラーの深刻度 40 | 41 | PHPのエラーは、何段階かの深刻度レベルに別れている。PHPでよく使われるメッセージの形式は、 42 | エラー(error)と注意(notice)そして警告(warning)だ。 43 | それぞれ別々の深刻度レベルが設定されていて、 `E_ERROR`、`E_NOTICE`、そして `E_WARNING` になる。 44 | 「エラー」は実行時の致命的な問題で、ふつうはコードの書きかたがまずいせいで発生する。 45 | これは修正しなければいけない。というのも、これが発生するとPHPの実行がそこで止まってしまうからだ。 46 | 「注意」は助言みたいなもので、問題を起こす可能性があるスクリプトを実行したときに発生する。 47 | スクリプトの実行は止まらない。 48 | 「警告」は致命的ではない問題で、これもスクリプトの実行は止まらない。 49 | 50 | もうひとつ、コンパイル時に発生する `E_STRICT` という形式のメッセージもある。 51 | これは、相互運用性や将来のバージョンのPHPとの互換性を考えたときに、コードを書き換えたほうがいいと提案するためのメッセージだ。 52 | 53 | ### PHPのエラー報告の挙動の変更 54 | 55 | エラー報告の挙動は、PHPの設定で変更することもできるしPHPの関数で変更することもできる。 56 | 組み込みの関数 `error_reporting()` にエラーレベル定数を渡せば、そのスクリプトの実行中に 57 | どのレベルのエラー処理をするのかを設定できる。 58 | たとえば、エラー(error)とか警告(warning)は表示させたいけれども、別に注意(notice)は見たくないという場合は、こんなふうにすればいい。 59 | 60 | {% highlight php %} 61 | upload->get_error()` みたいなメソッドを実行することになる。 19 | 何が問題かというと、まずエラーが発生したのかどうかを自分で調べないといけないこと。 20 | そして次に、エラー情報を取得する方法をドキュメントで調べないといけないこと。 21 | もっとはっきりわかるようにしてくれたらいいのに。 22 | 23 | 別の問題もある。何かのクラスがエラーを画面に投げっぱなしにしてそのまま終わるような場合だ。 24 | そんなことをすれば、エラーがあったときにプログラム中で動的に対応することができなくなってしまう。 25 | そんな場合は、エラーではなく例外を発生させないといけない。 26 | 例外にしておけば開発者がエラーに気づけるし、プログラムの中で対応できるようになる。 27 | たとえばこんな感じだ。 28 | 29 | {% highlight php %} 30 | subject('タイトル'); 33 | $email->body('ごきげんいかが?'); 34 | $email->to('guy@example.com', '誰かさん'); 35 | 36 | try 37 | { 38 | $email->send(); 39 | } 40 | catch(Fuel\Email\ValidationFailedException $e) 41 | { 42 | // 検証に失敗した 43 | } 44 | catch(Fuel\Email\SendingFailedException $e) 45 | { 46 | // ドライバがメールを送れなかった 47 | } 48 | finally 49 | { 50 | // 例外が発生してもしなくても、ここは必ず実行される 51 | } 52 | {% endhighlight %} 53 | 54 | ### SPL の例外 55 | 56 | 汎用的な `Exception` クラスには、開発者がデバッグするためのコンテキスト情報がほとんど含まれていない。 57 | これを改善するには、特化型の `Exception` を作ればいい。つまり、`Exception` クラスのサブクラスを作るってことだ。 58 | 59 | {% highlight php %} 60 | 大量に できあがってしまうかもしれないが、 66 | [SPL 拡張モジュール][splext] が用意する例外クラスを使えば少しはましになるだろう。 67 | 68 | たとえば、マジックメソッド `__call()` を使っているときに、無効なメソッドを要求されたとしよう。 69 | 標準の Exception クラスを使うのは曖昧すぎるし、そのためだけに専用の例外クラスを作るのも何だし、 70 | という場合には単に `throw new BadMethodCallException;` とすればよい。 71 | 72 | * [例外について][exceptions] 73 | * [SPL 拡張モジュール][splexe] 74 | * [PHP での例外のネスト][nesting-exceptions-in-php] 75 | 76 | 77 | [splext]: /#standard_php_library 78 | [exceptions]: https://www.php.net/language.exceptions 79 | [splexe]: https://www.php.net/spl.exceptions 80 | [nesting-exceptions-in-php]: https://www.brandonsavage.net/exceptional-php-nesting-exceptions-in-php/ 81 | -------------------------------------------------------------------------------- /_posts/10-01-01-Security.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: セキュリティ 3 | anchor: security 4 | --- 5 | 6 | # セキュリティ {#security_title} 7 | 8 | 今まで見たPHPのセキュリティに関する資料の中でいちばんだったのは、[Paragon Initiative](https://paragonie.com/) による 9 | [The 2018 Guide to Building Secure PHP Software](https://paragonie.com/blog/2017/12/2018-guide-building-secure-php-software)だ。 10 | -------------------------------------------------------------------------------- /_posts/10-02-01-Web-Application-Security.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ウェブアプリケーションのセキュリティ 3 | isChild: true 4 | anchor: web_application_security 5 | --- 6 | 7 | ## ウェブアプリケーションのセキュリティ {#web_application_security_title} 8 | 9 | PHP開発者は全員、[Webアプリケーションセキュリティの基本][4] を学ぶべきだ。内容としてはだいたいこんな感じになる。 10 | down into a handful of broad topics: 11 | 12 | 1. コードとデータの分離 13 | * データをコードとして実行するときにはSQLインジェクションやクロスサイトスクリプティングやファイルインクルード攻撃の恐れがある 14 | * コードをデータとして表示するときには情報漏洩の恐れがある (ソースコードが晒されてしまったり、C言語の場合ならアドレス空間配置のランダム化もある [ASLR][5]) 15 | 2. アプリケーションロジック 16 | * 認証・認可の制御不全 17 | * 入力の検証 18 | 3. 運用環境 19 | * PHPのバージョン 20 | * サードパーティのライブラリ 21 | * OS 22 | 4. 暗号化の弱点 23 | * [弱い乱数][6]. 24 | * [選択暗号文攻撃][7]. 25 | * [サイドチャネル情報漏洩][8]. 26 | 27 | 世の中には悪い人たちがいて、あなたの書いたウェブアプリケーションもきっと狙われている。 28 | 必要な対策をして、ウェブアプリケーションのセキュリティを固めておくことが大切だ。 29 | ありがたいことに、[The Open Web Application Security Project][1] (OWASP) 30 | の人たちが、既知のセキュリティ問題とその対策をまとめてくれている。 31 | セキュリティが気になる開発者は必読だ。 32 | Padraic Bradyが書いた[Survive The Deep End: PHP Security][3] も、 33 | ウェブアプリケーションセキュリティに関してPHP向けに書かれたよいドキュメントだ。 34 | 35 | * [OWASP Security Guideを読む][2] 36 | 37 | 38 | [1]: https://www.owasp.org/ 39 | [2]: https://www.owasp.org/index.php/Guide_Table_of_Contents 40 | [3]: https://phpsecurity.readthedocs.io/en/latest/index.html 41 | [4]: https://paragonie.com/blog/2015/08/gentle-introduction-application-security 42 | [5]: https://www.techtarget.com/searchsecurity/definition/address-space-layout-randomization-ASLR 43 | [6]: https://paragonie.com/blog/2016/01/on-design-and-implementation-stealth-backdoor-for-web-applications 44 | [7]: https://paragonie.com/blog/2015/05/using-encryption-and-authentication-correctly 45 | [8]: https://blog.ircmaxell.com/2014/11/its-all-about-time.html 46 | -------------------------------------------------------------------------------- /_posts/10-03-01-Password-Hashing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: パスワードのハッシュ処理 3 | isChild: true 4 | anchor: password_hashing 5 | --- 6 | 7 | ## パスワードのハッシュ処理 {#password_hashing_title} 8 | 9 | 誰もがいつかは、ログイン機能を持つ PHP アプリケーションを書くことになる。 10 | ユーザー名とパスワード (のハッシュ) をデータベースに保存して、 11 | ユーザーのログイン時にそれを使って認証するというやつだ。 12 | 13 | データベースにパスワードを保存するときは、適切に [_ハッシュ_][3] することが大切だ。 14 | ハッシュと暗号化は [まったく違うもの][7] なのに、混同されることが多い。 15 | 16 | パスワードのハッシュは不可逆な操作で、ユーザーのパスワードに対して一方通行で行う。 17 | できあがる結果は固定長の文字列で、元には戻せない。 18 | つまり、このハッシュを別のハッシュと比較すれば元の文字列どうしが一致するかどうかは判断できるが、 19 | 元の文字列が何だったかはわからないってことだ。 20 | パスワードをハッシュせずにデータベースに保存していると、 21 | 万一第三者に不正アクセスされた場合に、すべてのユーザーアカウントが乗っ取られてしまう。 22 | 23 | ハッシュと違って暗号化は元に戻せる (鍵さえ手元にあればね)。 24 | 暗号化そのものは使う場面を選べば便利なものだけど、ことパスワードの保存に関してはうまい手段ではない。 25 | 26 | パスワードには個別に [_ソルト_][5] が必要だ。ランダムな文字列をパスワードに付けてからハッシュするってこと。 27 | そうしておけば、辞書攻撃から守れるし「レインボーテーブル(ありがちなパスワードとそのハッシュをまとめた変換テーブル)」による攻撃も防げる。 28 | 29 | ハッシュとソルトは欠かせない。だって、たいていのユーザーはいろんなサービスでパスワードを使い回すものだし、 30 | パスワード自体も決して強力なものだとは言えないから。 31 | 32 | あと、速度が売りの汎用ハッシュ関数 (SHA256とか) じゃなくて [_パスワードのハッシュ_ に特化したアルゴリズム][6] を使うこと。 33 | 2018年6月時点でパスワードのハッシュに使ってもかまわないアルゴリズムはこんな感じ。 34 | 35 | * Argon2 (PHP 7.2 以降で使える) 36 | * Scrypt 37 | * **Bcrypt** (PHP が用意してくれている。詳細は後ほど) 38 | * PBKDF2 と HMAC-SHA256 あるいは HMAC-SHA512 の組み合わせ 39 | 40 | ありがたいことに、最近の PHP ならこのあたりも使いやすい。 41 | 42 | **`password_hash`によるパスワードのハッシュ** 43 | 44 | PHP 5.5からは、新たに`password_hash()`関数が使えるようになった。 45 | 現時点では、この関数はBCryptを使っている。これは、現在のPHPがサポートしているアルゴリズムの中では最強のものだ。 46 | 必要に応じて、将来はもっと強力なアルゴリズムをサポートするように更新されるだろう。 47 | この関数をPHP 5.5より前のバージョンでも使えるようにするため、`password_compat`ライブラリも作られた。 48 | このライブラリはPHP 5.3.7以降で使える。 49 | 50 | この例では、文字列をハッシュした後でそのハッシュを新たな文字列と比較している。 51 | 二つの文字列は違っている('secret-password'と'bad-password')ので、このログインは失敗する。 52 | 53 | {% highlight php %} 54 | = 5.3.7 && < 5.5 で使える `password_compat`] [2] 70 | * [暗号学的なハッシュについて調べる] [3] 71 | * [ソルトについて調べる] [5] 72 | * [PHP `password_hash()` RFC] [4] 73 | 74 | 75 | [1]: https://www.php.net/function.password-hash 76 | [2]: https://github.com/ircmaxell/password_compat 77 | [3]: http://ja.wikipedia.org/wiki/暗号学的ハッシュ関数 78 | [4]: https://wiki.php.net/rfc/password_hash 79 | [5]: https://wikipedia.org/wiki/Salt_(cryptography) 80 | [6]: https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016 81 | [7]: https://paragonie.com/blog/2015/08/you-wouldnt-base64-a-password-cryptography-decoded 82 | 83 | -------------------------------------------------------------------------------- /_posts/10-04-01-Data-Filtering.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: データのフィルタリング 3 | isChild: true 4 | anchor: data_filtering 5 | --- 6 | 7 | ## データのフィルタリング {#data_filtering_title} 8 | 9 | PHP のコードに外部から渡される入力は、絶対に信用してはいけない。 10 | 外部からの入力は、常に検証してから使うようにしよう。 11 | `filter_var()` 関数や `filter_input()` 関数で、入力の検証や書式の判定 (メールアドレスなど) 12 | ができる。 13 | 14 | 外部からの入力にはいろいろな種類がある。フォームから渡される `$_GET` や `$_POST` もあれば、 15 | スーパーグローバル `$_SERVER` に含まれるものもある。そして 16 | `fopen('php://input', 'r')` でやってくる HTTP リクエストのボディもそうだ。 17 | 外部からの入力といっても、ユーザーがフォームで入力したものばかりとは限らないことに注意。 18 | アップロードしたりダウンロードしたりしたファイル、セッションのデータ、 19 | クッキーのデータ、サードパーティのウェブサービスからのデータ。 20 | これらはみんな外部からの入力となる。 21 | 22 | 外部からのデータをいったん保存して、何かと組み合わせて後で使うとしよう。 23 | それでも、そのデータが外部からの入力であるという事実は変わらない。 24 | そのデータを処理したり出力したり何かとつなげたりコードに組み込んだりするときには 25 | 「適切にフィルタリングできてる?」「信頼できる?」と確認しよう。 26 | 27 | データのフィルタリング方法は、その利用目的によって異なる。 28 | たとえば、外部の入力を何も処理せずに HTML ページに出力すると、 29 | あなたのサイト上で任意の JavaScript が実行できてしまうことになる! 30 | これが、いわゆるクロスサイトスクリプティング (XSS) である。 31 | とても危険な攻撃だ。こんなときに XSS を回避する方法のひとつは、 32 | `strip_tags()` で入力からすべての HTML タグを取り除くか、 33 | あるいは `htmlentities()` や `htmlspecialchars()` でエスケープして HTML エンティティに変換することだ。 34 | 35 | 別の例として、外部の入力をコマンドラインのオプションとして渡すことを考えよう。 36 | これって非常に危険なことだし、ふつうはあまりやるべきではないことだ。 37 | でも、もしやるなら、組み込みの関数 `escapeshellarg()` 38 | を使えばコマンドの引数を実行されてしまうことが防げる。 39 | 40 | 最後の例は、外部の入力に基づいてファイルシステム上のファイルを読み込むというものだ。 41 | このときは、ファイル名のかわりにファイルパスを渡されてしまうという攻撃が考えられる。 42 | 外部の入力から"/"や"../"、[null バイト][6]などを取り除いて、 43 | 隠しファイルや公開すべきでない場所のファイルを読み込まないようにする必要がある。 44 | 45 | * [データのフィルタリング][1] 46 | * [`filter_var` 関数][4] 47 | * [`filter_input` 関数][5] 48 | * [null バイトの扱い][6] 49 | 50 | ### サニタイズ 51 | 52 | サニタイズとは、外部の入力から危険な文字を取り除く (あるいはエスケープする) ことだ。 53 | 54 | たとえば、外部の入力を HTML に含めたり SQL クエリに組み込んだりする前に、 55 | サニタイズが必要となる。[PDO](#databases) でバインド変数を使う場合は、 56 | PDO が入力をサニタイズする。 57 | 58 | 外部の入力を HTML として組み込むときに、いくつかの安全な HTML 59 | タグはそのまま使わせたいという場合もある。そんな要求を実現するのは非常に難しいので、 60 | たいていの場合は Markdown や BBCode などのフォーマットを代替手段として使うのだが、 61 | どうしてもという場合は [HTML Purifier][html-purifier] 62 | のようなホワイトリストライブラリを使える。 63 | 64 | [サニタイズフィルター][2] 65 | 66 | ### アンシリアライズ 67 | 68 | ユーザーからの入力など、信頼できないところから渡されたデータを `unserialize()` するのは危険だ。 69 | 悪意のあるユーザーが送り込んだオブジェクト(ユーザー定義のプロパティつきのもの)のインスタンスを生成できてしまい、 70 | **たとえそのオブジェクトを一切使わなくても** そのデストラクタは実行されてしまう。 71 | なので、信頼できないデータのアンシリアライズは避けるべきだ。 72 | 73 | シリアル化したデータをユーザーに渡す必要がある場合は、([`json_decode`][json_decode] や [`json_encode`][json_encode] 経由で) JSON のような安全で標準的なデータ交換フォーマットを使うようにしよう。 74 | 75 | ### バリデーション 76 | 77 | バリデーションとは、外部の入力が期待通りであるかどうかを確かめること。 78 | たとえばユーザー登録の処理では、 79 | メールアドレスや電話番号、あるいは年齢などを検証することになるだろう。 80 | 81 | [バリデーションフィルター][3] 82 | 83 | 84 | [1]: https://www.php.net/book.filter 85 | [2]: https://www.php.net/filter.filters.sanitize 86 | [3]: https://www.php.net/filter.filters.validate 87 | [4]: https://www.php.net/function.filter-var 88 | [5]: https://www.php.net/function.filter-input 89 | [6]: https://www.php.net/security.filesystem.nullbytes 90 | [html-purifier]: http://htmlpurifier.org/ 91 | [json_decode]: https://www.php.net/manual/function.json-decode.php 92 | [json_encode]: https://www.php.net/manual/function.json-encode.php 93 | -------------------------------------------------------------------------------- /_posts/10-05-01-Configuration-Files.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 設定ファイル 3 | isChild: true 4 | anchor: configuration_files 5 | --- 6 | 7 | ## 設定ファイル {#configuration_files_title} 8 | 9 | 自作のアプリケーションで設定ファイルを使うときには、これらの指針に従うのがお勧めだ。 10 | 11 | - 設定情報に直接アクセスしたり、ファイルシステム経由で取り込んだりできないようにする。 12 | - どうしてもドキュメントルートに設定ファイルを置かざるを得ないのなら、そのファイルの拡張子を`.php`にする。 13 | そうすれば、仮にそのファイルへ直接アクセスされたとしても、中身がそのまま見えてしまうことはない。 14 | - 設定ファイル内の情報は適切に保護すること。暗号化するなり、ファイルシステム上のパーミッションをきちんと設定するなりしておく。 15 | - パスワードやAPIトークンなどの機密情報を含んだ設定ファイルは、ソース管理システムに含めないようにする。 16 | -------------------------------------------------------------------------------- /_posts/10-06-01-Register-Globals.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Register Globals 3 | isChild: true 4 | anchor: register_globals 5 | --- 6 | 7 | ## Register Globals {#register_globals_title} 8 | 9 | **注意:** 10 | PHP 5.4.0 からは `register_globals` 11 | という設定項目がなくなったので、この設定は使えない。 12 | このページは単に、大昔のアプリケーションをアップグレードしている人たち向けの警告として用意したものでしかない。 13 | 14 | `register_globals`を有効にすると、`$_POST`や`$_GET`そして`$_REQUEST` 15 | などの内容にアプリケーションのグローバルスコープでアクセスできるようになる。 16 | これを使うとセキュリティの問題が発生しやすくなる。 17 | というのも、そのデータがどこからきたものなのかをアプリケーション側で判断できなくなるからだ。 18 | 19 | たとえば `$_GET['foo']` の内容に `$foo` でアクセスできることになるのだが、 20 | これは、宣言済みの変数の中身を自動的に上書きしてしまうことにつながる。 21 | PHP 5.4.0 より前のバージョンを使っている場合は、 22 | __確実に__ `register_globals` を __off__ にしておこう。 23 | -------------------------------------------------------------------------------- /_posts/10-07-01-Error-Reporting.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: エラーレポート 3 | isChild: true 4 | anchor: error_reporting 5 | --- 6 | 7 | ## エラーレポート {#error_reporting_title} 8 | 9 | エラーを記録しておくと、アプリケーションに何か問題があったときにその原因を見つけやすくなる。 10 | しかしその一方で、アプリケーションの構造に関する情報を外部に公開してしまうことにもなる。 11 | エラーメッセージを出すことで起こる問題からアプリケーションを守るには、 12 | 開発環境と本番環境でサーバーの設定を切り替える必要がある。 13 | 14 | ### 開発環境 15 | 16 | **開発** 環境で、起こりうるエラーをすべて表示するときには、`php.ini`で次のように設定する。 17 | 18 | {% highlight ini %} 19 | display_errors = On 20 | display_startup_errors = On 21 | error_reporting = -1 22 | log_errors = On 23 | {% endhighlight %} 24 | 25 | > 値に`-1`を指定すると、仮に将来のバージョンのPHPで新しいレベルと定数が追加されたとしてもすべてのエラーを表示するようになります。E_ALL 定数も、PHP 5.4以降これと同じ挙動になります。 - [php.net](https://www.php.net/function.error-reporting) 26 | 27 | `E_STRICT`エラーレベル定数は5.3.0で導入されたもので、当時は 28 | `E_ALL`には含まれていなかった。でも5.4.0からは`E_ALL`に含まれるようになった。 29 | だからどうなんだって? 30 | あらゆるエラーを表示させたいときには、5.3の場合は 31 | `-1`あるいは`E_ALL | E_STRICT`を使わないといけないってことだ。 32 | 33 | **PHPのバージョン別の、すべてのエラーを表示させるための設定** 34 | 35 | * < 5.3 `-1` or `E_ALL` 36 | *   5.3 `-1` or `E_ALL | E_STRICT` 37 | * > 5.3 `-1` or `E_ALL` 38 | 39 | ### 本番環境 40 | 41 | **本番** 環境でエラーの情報を見せないようにするには、`php.ini`で次のように設定する。 42 | 43 | {% highlight ini %} 44 | display_errors = Off 45 | display_startup_errors = Off 46 | error_reporting = E_ALL 47 | log_errors = On 48 | {% endhighlight %} 49 | 50 | この本番環境用の設定をしても、ウェブサーバーのエラーログにはエラーの内容がきちんと残る。 51 | しかし、ユーザーにはエラーが見えなくなる。これらの設定項目についてもっと詳しく知りたければ、 52 | PHP のマニュアルを読もう。 53 | 54 | * [error_reporting](https://www.php.net/errorfunc.configuration#ini.error-reporting) 55 | * [display_errors](https://www.php.net/errorfunc.configuration#ini.display-errors) 56 | * [display_startup_errors](https://www.php.net/errorfunc.configuration#ini.display-startup-errors) 57 | * [log_errors](https://www.php.net/errorfunc.configuration#ini.log-errors) 58 | -------------------------------------------------------------------------------- /_posts/11-01-01-Testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: テスト 3 | anchor: testing 4 | --- 5 | 6 | # テスト {#testing_title} 7 | 8 | PHPのコードを書くときには、自動化されたテストも書くのがよい習慣だとされている。 9 | そうすれば、頑丈なアプリケーションが作れるようになる。自動テストを活用すれば、 10 | 何かを変更したり機能を追加したりしたときにもアプリケーションがきちんと動くことを確認できる。 11 | 欠かせないツールだ。 12 | 13 | PHP で使えるテスト用ツール (あるいはフレームワーク) にはいくつかのものがあり、 14 | それぞれ異なる手法を使っている。が、目指すところは同じ。 15 | 手作業でのテストをなくす、そして最新の変更で既存の機能を壊していないかどうかを確かめるためだけに 16 | 大規模な品質保証チームを使うなんてことをなくす、というのが目標だ。 17 | -------------------------------------------------------------------------------- /_posts/11-02-01-Test-Driven-Development.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: テスト駆動開発 3 | isChild: true 4 | anchor: test_driven_development 5 | --- 6 | 7 | ## テスト駆動開発 {#test_driven_development_title} 8 | 9 | [Wikipedia](https://wikipedia.org/wiki/Test-driven_development) によると、 10 | 11 | > Test-driven development (TDD) is a software development process that relies on the repetition of a very short 12 | > development cycle: first the developer writes a failing automated test case that defines a desired improvement or new 13 | > function, then produces code to pass that test and finally refactors the new code to acceptable standards. Kent Beck, 14 | > who is credited with having developed or 'rediscovered' the technique, stated in 2003 that TDD encourages simple 15 | > designs and inspires confidence. 16 | 17 | アプリケーションのテストには、いくつかの種類がある。 18 | 19 | ### ユニットテスト 20 | 21 | ユニットテストとはプログラミングの手法のひとつだ。 22 | 関数やクラスやメソッドが期待通りに動いていることを開発中に常に確かめる。 23 | さまざまな関数やメソッドの入出力の値をチェックすれば、 24 | その内部ロジックが正しく動いていることを確認できる。 25 | 依存性注入の仕組みを利用してクラスのモックやスタブを使えば、 26 | 依存ライブラリが正しく使われていることを確かめられる。 27 | 28 | クラスや関数を作るときに、その振る舞いを確かめるためのユニットテストも同時に作る。 29 | 最も基本的なレベルだと、間違った引数を渡した場合にエラーになることや、 30 | 正しい引数を渡したときに正常に動くことなどを確認しないといけない。 31 | こうしておけば、後にクラスや関数に手を加えたときにも 32 | 今までの機能が期待通りに動くかどうかを確かめられるようになる。 33 | test.php で `var_dump()` とかいうやり方もあるけど、 34 | まともなアプリケーションを作るつもりならそれはあり得ない。 35 | 36 | それ以外にもユニットテストの使い道はある。オープンソースに貢献する手段として使えるのだ。 37 | うまく機能していないことを示すためにテストを書く。そして動くように修正する。 38 | 最後にテストが通ることを確認する。こんなパッチを送れば、きっと受け入れてもらいやすくなるだろう。 39 | もし何かプロジェクトを運営していて pull request を受け付けているのなら、 40 | 「パッチにはテストをつけること」という条件をつけておくといいだろう。 41 | 42 | [PHPUnit](https://phpunit.de)は、PHPアプリケーションでユニットテストを書くための 43 | デファクトスタンダードのフレームワークだ。しかしそれ以外にも選択肢がある。 44 | 45 | * [atoum](https://github.com/atoum/atoum) 46 | * [Kahlan](https://github.com/kahlan/kahlan) 47 | * [Peridot](https://peridot-php.github.io/) 48 | * [Pest](https://pestphp.com/) 49 | * [SimpleTest](https://github.com/simpletest/simpletest) 50 | 51 | ### インテグレーションテスト 52 | 53 | [Wikipedia](https://wikipedia.org/wiki/Integration_testing) によると、 54 | 55 | > Integration testing (sometimes called Integration and Testing, abbreviated "I&T") is the phase in software testing in 56 | > which individual software modules are combined and tested as a group. It occurs after unit testing and before 57 | > validation testing. Integration testing takes as its input modules that have been unit tested, groups them in larger 58 | > aggregates, applies tests defined in an integration test plan to those aggregates, and delivers as its output the 59 | > integrated system ready for system testing. 60 | 61 | ユニットテスト用のツールの多くはインテグレーションテストにも使える。 62 | ほぼ同じような指針で行うものだからである。 63 | 64 | ### 機能テスト 65 | 66 | 受け入れテストと呼ばれることもある。実際にアプリケーションを使う観点での自動テストを作ってその動きを確認する。 67 | 単にコード片が正しく動くとか、個々のパーツがお互いに正しくやりとりできるかとかいうレベルのテストではない。 68 | このレベルのテストでは、実際のデータを使ったりアプリケーションの実際のユーザーをシミュレートしたりすることが一般的だ。 69 | 70 | #### 機能テスト用のツール 71 | 72 | * [Selenium](https://www.selenium.dev/) 73 | * [Mink](https://mink.behat.org/) 74 | * [Codeception](https://codeception.com/) これはフルスタックのテスティングフレームワークで、受け入れテスト用のツール群も含んでいる 75 | * [Storyplayer](https://datasift.github.io/storyplayer/) これはフルスタックのテスティングフレームワークで、テスト環境をオンデマンドで作ったり破棄したりする機能も含んでいる 76 | -------------------------------------------------------------------------------- /_posts/11-03-01-Behavior-Driven-Development.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 振る舞い駆動開発 3 | isChild: true 4 | anchor: behavior_driven_development 5 | --- 6 | 7 | ## 振る舞い駆動開発 {#behavior_driven_development_title} 8 | 9 | 振る舞い駆動開発 (BDD) には二種類ある。SpecBDD と StoryBDD だ。 10 | SpecBDD はコードの技術的な振る舞いを重視し、StoryBDD は業務的あるいは機能的な振る舞いを重視する。 11 | PHP には、これら二種類の BDD 用のフレームワークが存在する。 12 | 13 | StoryBDD では、人間が読める形式のストーリーを書いてアプリケーションの振る舞いを表す。 14 | そしてそのストーリーを、アプリケーションのテストとして実行する。 15 | PHP アプリケーションで StoryBDD をするために使えるフレームワークが 16 | [Behat] で、これは Ruby の [Cucumber] の影響を受けたフレームワークである。 17 | Gherkin DSL を使ってフィーチャを記述できる。 18 | 19 | SpecBDD では、実際のコードのあるべき振る舞いをスペックとして書く。 20 | 関数やメソッドを単独でテストするのではなく、その関数やメソッドがどのように振る舞うのかを記述するのだ。 21 | PHP で SpecBDD をするときに使えるフレームワークが [PHPSpec] で、これは 22 | Ruby の [RSpec project][Rspec] の影響を受けている。 23 | 24 | ### BDD に関するリンク 25 | 26 | * [Behat] は PHP 用の StoryBDD フレームワークで、Ruby の [Cucumber] プロジェクトの影響を受けている。 27 | * [PHPSpec] は PHP 用の SpecBDD フレームワークで、Ruby の [RSpec] プロジェクトの影響を受けている。 28 | * [Codeception] はフルスタックのテストフレームワークで、BDD の原則に従っている。 29 | 30 | 31 | [Behat]: https://behat.org/ 32 | [Cucumber]: https://cucumber.io/ 33 | [PHPSpec]: https://www.phpspec.net/ 34 | [RSpec]: https://rspec.info/ 35 | [Codeception]: https://codeception.com/ 36 | -------------------------------------------------------------------------------- /_posts/11-04-01-Complementary-Testing-Tools.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: その他のテスト用ツール 3 | isChild: true 4 | anchor: complementary_testing_tools 5 | --- 6 | 7 | ## その他のテスト用ツール {#complementary_testing_tools_title} 8 | 9 | これまでに取り上げたテストツールや振る舞い駆動開発フレームワーク以外にも、 10 | いろいろなフレームワークやヘルパーライブラリがある。これらも有用に使える。 11 | 12 | ### ツールへのリンク 13 | 14 | * [Selenium] はブラウザ自動化ツールで、[PHPUnit と組み合わせて使える]。 15 | * [Mockery] はモックオブジェクトフレームワークで、[PHPUnit] や [PHPSpec] と組み合わせて使える。 16 | * [Prophecy] は強力で柔軟な PHP 用モックオブジェクトフレームワークで、[PHPSpec] に組み込まれている。さらに [PHPUnit] と組み合わせても使える。 17 | * [php-mock] はPHPの組み込み関数のモックを作りやすくするためのライブラリだ。 18 | * [Infection] は [ミューテーション解析] をPHPで実装したもので、自分が書いたテストがどれくらい効果的かを測定してくれる。 19 | * [PHPUnit Polyfills] は、様々なPHPUnitのバージョンでテストスイートを実行する必要がある場合に、PHPUnitの複数バージョンで互換性があるテストを生成できるライブラリだ。 20 | 21 | 22 | [Selenium]: https://www.selenium.dev/ 23 | [PHPUnit と組み合わせて使える]: https://github.com/giorgiosironi/phpunit-selenium/ 24 | [Mockery]: https://github.com/padraic/mockery 25 | [PHPUnit]: https://phpunit.de/ 26 | [PHPSpec]: https://www.phpspec.net/ 27 | [Prophecy]: https://github.com/phpspec/prophecy 28 | [php-mock]: https://github.com/php-mock/php-mock 29 | [Infection]: https://github.com/infection/infection 30 | [ミューテーション解析]: https://ja.wikipedia.org/wiki/%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E8%A7%A3%E6%9E%90 31 | [PHPUnit Polyfills]: https://github.com/Yoast/PHPUnit-Polyfills 32 | -------------------------------------------------------------------------------- /_posts/12-01-01-Servers-and-Deployment.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 各種サーバーへのデプロイ 3 | anchor: servers_and_deployment 4 | --- 5 | 6 | # 各種サーバーへのデプロイ {#servers_and_deployment_title} 7 | 8 | PHP のアプリケーションをデプロイして本番サーバーで運用するための方法を紹介する。 9 | -------------------------------------------------------------------------------- /_posts/12-02-01-Platform-as-a-Service.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Platform as a Service (PaaS) 3 | isChild: true 4 | anchor: platform_as_a_service 5 | --- 6 | 7 | ## Platform as a Service (PaaS) {#platform_as_a_service_title} 8 | 9 | PaaS を使えば、PHP アプリケーションをウェブ上で動かすために必要なシステムやネットワーク環境を用意してくれる。 10 | ほとんど何も設定せずに、PHP のアプリケーションやフレームワークを実行できるということだ。 11 | 12 | 最近は、PHP アプリケーションのデプロイ先として PaaS を使うことが多くなった。あらゆる規模のアプリケーションを扱える。 13 | [PHP 用の PaaS "Platform as a Service" プロバイダ](#php_paas_providers) を、[情報源](#resources) 14 | にまとめた。 15 | -------------------------------------------------------------------------------- /_posts/12-03-01-Virtual-or-Dedicated-Servers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 仮想サーバー(専用サーバー) 3 | isChild: true 4 | anchor: virtual_or_dedicated_servers 5 | --- 6 | 7 | ## 仮想サーバーあるいは専用サーバー {#virtual_or_dedicated_servers_title} 8 | 9 | サーバー管理が苦にならない人、あるいはサーバー管理を勉強してみたい人は、仮想サーバーあるいは専用サーバーを選ぶといい。 10 | そうすれば、アプリケーションの運用環境を完全に制御できる。 11 | 12 | ### nginx と PHP-FPM 13 | 14 | PHP に組み込まれた FastCGI Process Manager (FPM) は [nginx] 15 | と組み合わせるのに最適だ。nginx は、軽量でパフォーマンスに優れたウェブサーバーである。 16 | Apache よりも少ないメモリで動き、同時にさばけるリクエストの数も多い。 17 | これは特に、共有メモリの少ない仮想サーバーでは重要だ。 18 | 19 | * [nginx][nginx] 20 | * [PHP-FPM][phpfpm] 21 | * [nginx と PHP-FPM で安全な環境を作る][secure-nginx-phpfpm] 22 | 23 | ### Apache と PHP 24 | 25 | PHP と Apache は長い付き合いだ。 26 | Apache はいろんな設定が可能で、さまざまな [モジュール][apache-modules] 27 | で機能を拡張できる。共有サーバーで PHP のフレームワークを動かしたり、WordPress 28 | みたいなアプリケーションを動かしたりするときにはよく使われる選択肢だ。 29 | 残念ながら Apache は、デフォルトでは nginx よりもメモリを食うし、 30 | 同時にさばけるユーザー数も nginx より少ない。 31 | 32 | Apache で PHP を動かすにはいくつかの選択肢がある。 33 | 一番よく使われていて簡単に設定できるのが、[prefork MPM] と `mod_php` の組み合わせだ。 34 | メモリの使用効率はそれほどよくないが、とりあえず動かして使うには一番シンプルだ。 35 | サーバー管理方面にあまり足を突っ込みたくない場合は、この方法がいいだろう。 36 | 注意すべき点は、`mod_php` を使う場合は必ず prefork MPM を使わないといけないということだ。 37 | 38 | Apache 本来のパフォーマンスや安定性をもっと絞り出したいという場合は、nginx と同じように FPM を使うこともできる。 39 | この場合は、[worker MPM] あるいは 40 | [event MPM] に mod_fastcgi あるいは mod_fcgid 41 | を組み合わせる。この設定はメモリの利用効率がよくて高速に動作するが、設定に手間がかかる。 42 | 43 | Apache 2.4 以降なら、[mod_proxy_fcgi]が使える。簡単にセットアップできるし高性能だ。 44 | 45 | * [Apache][apache] 46 | * [Multi-Processing Modules][apache-MPM] 47 | * [mod_fastcgi][mod_fastcgi] 48 | * [mod_fcgid][mod_fcgid] 49 | * [mod_proxy_fcgi][mod_proxy_fcgi] 50 | * [Apache、PHP-FPM、mod_proxy_fcgiの環境のセットアップ][tutorial-mod_proxy_fcgi] 51 | 52 | 53 | [nginx]: https://nginx.org/ 54 | [phpfpm]: https://www.php.net/install.fpm 55 | [secure-nginx-phpfpm]: https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/ 56 | [apache-modules]: https://httpd.apache.org/docs/2.4/mod/ 57 | [prefork MPM]: https://httpd.apache.org/docs/2.4/mod/prefork.html 58 | [worker MPM]: https://httpd.apache.org/docs/2.4/mod/worker.html 59 | [event MPM]: https://httpd.apache.org/docs/2.4/mod/event.html 60 | [apache]: https://httpd.apache.org/ 61 | [apache-MPM]: https://httpd.apache.org/docs/2.4/mod/mpm_common.html 62 | [mod_fastcgi]: https://blogs.oracle.com/opal/post/php-fpm-fastcgi-process-manager-with-apache-2 63 | [mod_fcgid]: https://httpd.apache.org/mod_fcgid/ 64 | [mod_proxy_fcgi]: https://httpd.apache.org/docs/current/mod/mod_proxy_fcgi.html 65 | [tutorial-mod_proxy_fcgi]: https://serversforhackers.com/video/apache-and-php-fpm 66 | -------------------------------------------------------------------------------- /_posts/12-04-01-Shared-Servers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 共有サーバー 3 | isChild: true 4 | anchor: shared_servers 5 | --- 6 | 7 | ## 共有サーバー {#shared_servers_title} 8 | 9 | PHP の人気のおかげで、いろんな共有サーバーで PHP が使える。 10 | むしろ PHP が使えない共有サーバーを見つけるほうが難しいだろう。 11 | ただし、最新バージョンが使えるかどうかは要注意だ。 12 | 共有サーバーでは、あなただけでなく他の開発者も同じマシンにウェブサイトをデプロイする。 13 | その利点は、安上がりに使えるということだ。 14 | ただ欠点もあって、同じサーバーに同居しているお隣さんが何をしでかすかがわからない。 15 | めちゃめちゃ負荷のかかることをしてしまったり、セキュリティホールを作り込んでしまったりといった恐れがある。 16 | もし充分な予算があるのなら、できるだけ共有サーバーは避けよう。 17 | 18 | 共有サーバーでは、最新バージョンのPHPが使えることを必ず確認すること。 19 | -------------------------------------------------------------------------------- /_posts/12-05-01-Building-your-Application.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: アプリケーションのビルドとデプロイ 3 | isChild: true 4 | anchor: building_and_deploying_your_application 5 | --- 6 | 7 | ## アプリケーションのビルドとデプロイ {#building_and_deploying_your_application_title} 8 | 9 | まさか、データベースのスキーマを変更したりテストを実行したりとかいったことを手作業でやってるなんてことはないよね? 10 | ちょっと待った!新しいバージョンのアプリケーションをデプロイするときに手作業がひとつでも増えると、 11 | 致命的な間違いを犯してしまう可能性もそのぶん増えてしまうんだ。たとえ単純な更新作業だとしても、 12 | きちんとしたビルド手順にしたがうこと。継続的インテグレーションの戦略にしたがって、 13 | [ビルドの自動化][buildautomation] をしておくといい。 14 | 15 | 自動化できるタスクには、こんなものがある。 16 | 17 | * 依存関係の管理 18 | * 必要な資産のコンパイル 19 | * テストの実行 20 | * ドキュメントの生成 21 | * パッケージング 22 | * デプロイ 23 | 24 | 25 | ### デプロイツール 26 | 27 | デプロイツールとは、ソフトウェアのデプロイにからむありがちな作業を処理するスクリプトをまとめたものだと言える。 28 | デプロイツール自体は君が作るソフトウェアの一部ではない。ソフトウェアを「外部から」支援するものだ。 29 | 30 | ビルド自動化やデプロイの助けとなるオープンソースのツールがたくさん公開されている。PHPで書かれているものもあれば、 31 | そうじゃないものもある。PHP製じゃないからといって、それを使わない理由はない。 32 | もし自分のやりたいことに適したツールがあるのなら、使うべきだ。いくつか例をあげよう。 33 | 34 | [Phing] を使えば、パッケージングやデプロイそしてテストといった処理をシンプルなXMLビルドファイルで設定できる。 35 | Phingは[Apache Ant] をベースに作られたもので、 36 | Webアプリのインストールやアップデートに必要となるタスク群を提供する。 37 | カスタムタスクで機能を追加することもでき、カスタムタスクはPHPで書ける。 38 | 古くからあるツールだけあって堅実で安定してるけど、ちょっと古臭さも感じる 39 | (設定をXMLファイルで管理するところとかね)。 40 | 41 | [Capistrano] は 42 | *中級から上級のプログラマー* 向けのシステムだ。構造化された、繰り返し可能な形式で、 43 | 複数のリモートマシン上でコマンドを実行できる。 44 | Ruby on Railsのアプリをデプロイするように設定されているが、 45 | PHP のアプリもデプロイできる。 46 | Capistranoを使いこなすには、RubyとRakeに関するそれなりの知識が必要だ。 47 | Dave Gardnerのblog記事[PHP Deployment with Capistrano][phpdeploy_capistrano] 48 | は、Capistranoに興味のあるPHP開発者への入門記事としておすすめだ。 49 | 50 | [Ansistrano] はデプロイプロセス(デプロイやロールバック) を簡単に管理するための Ansible のロールだ。PHP や Python や Ruby のようなスクリプト言語で書かれたアプリケーションで使える。これは、[Capistrano] を Ansible に移植したもので、既にたくさんの PHP を利用している会社で使われている。 51 | 52 | [Deployer] はPHPで書かれたデプロイツールで、シンプルかつ機能的だ。 53 | タスクを並列に実行し、アトミックなデプロイを行い、サーバー間の整合性を維持する。 54 | SymfonyやLaravel、Zend Framework、そしてYiiなどで使える、一般的なタスクのレシピが用意されている。 55 | Younes Rafieの記事[Easy Deployment of PHP Applications with Deployer][phpdeploy_deployer] 56 | は、Deployerを使ってアプリケーションをデプロイするためのよいチュートリアルになっている。 57 | 58 | [Magallanes] もPHPで書かれたツールで、YAMLでのシンプルな設定ができる。 59 | 複数サーバーや複数環境、アトミックなデプロイに対応していて、 60 | 一般的なツールやフレームワークで使える組み込みのタスクが用意されている。 61 | 62 | #### あわせて読みたい: 63 | 64 | * [Apache Antによるプロジェクトの自動化][apache_ant_tutorial] 65 | * [Expert PHP Deployments][expert_php_deployments] - CapistranoやPhing、Vagrantによるデプロイを扱ったフリーの書籍 66 | * [Deploying PHP Applications][deploying_php_applications] - PHPのデプロイに関するベストプラクティスやツールを扱った書籍 67 | 68 | ### サーバーの構成管理 69 | 70 | サーバーの構成管理は、大量のサーバーを扱うようになると特に大変なタスクだ。 71 | いろんなツールが用意されているので、こういったインフラの構築を自動化できる。 72 | ツールを使えば、適切なサーバーが適切な構成になっていることを確実にできる。 73 | これらのツールは大規模なクラウドホスティングプロバイダー 74 | (Amazon Web Services, Heroku, DigitalOceanなど) 75 | にもインスタンスの管理用に統合されていることが多くて、 76 | より簡単にアプリケーションをスケールできるようになっている。 77 | 78 | [Ansible] は、YAMLファイルでインフラを管理するツールだ。 79 | 気軽に使い始められるし、複雑で大規模なアプリケーションにも使える。 80 | クラウドのインスタンスを管理するためのAPIも用意されていて、 81 | 対応したツールを使えば動的インベントリを通じてインスタンスを管理できる。 82 | 83 | [Puppet] は、独自の言語やファイルタイプを使ってサーバーや構成を管理する。 84 | マスター/クライアント形式で使うこともできるし、「マスターレス」モードで使うこともできる。 85 | マスター/クライアントモードの場合は、所定のインターバルでクライアントが中央サーバーをポーリングして、 86 | 新しい構成が見つかったら自分自身を更新する。 87 | マスターレスモードでは、変更内容を各ノードにプッシュする。 88 | 89 | [Chef] はRubyで作られた強力なシステムインテグレーションフレームワークで、 90 | サーバー環境や仮想マシンをまるごと構築できる。 91 | Amazon Web Servicesとも統合されていて、OpsWorksというサービスを通じて利用する。 92 | 93 | #### あわせて読みたい: 94 | 95 | * [Ansibleチュートリアル][an_ansible_tutorial] 96 | * [Ansible for DevOps][ansible_for_devops] - Ansibleのすべてを扱った書籍 97 | * [Ansible for AWS][ansible_for_aws] - AnsibleとAmazon Web Servicesとの統合について扱った書籍 98 | * [LAMPアプリケーションのデプロイにChefやVagrantそしてEC2を使うというお題で書かれた全3回のシリーズ][chef_vagrant_and_ec2] 99 | * [Chefのクックブック。PHPのインストールと設定やPEARについて扱っている][Chef_cookbook] 100 | * [Chefのビデオチュートリアルシリーズ][Chef_tutorial] 101 | 102 | ### 継続的インテグレーション 103 | 104 | > 継続的インテグレーションはソフトウェア開発のプラクティスのひとつで、 105 | > チームのメンバーが自分たちの作業を頻繁に統合するというものだ。 106 | > 通常は、各自が少なくとも一日に一度は統合する。一日に何度も統合することもある。 107 | > 多くのチームが、この方針のおかげで統合時の問題が少なくなるし、 108 | > きちんとしたソフトウェアをより素早く開発できるようになると実感している。 109 | 110 | *-- マーティン・ファウラー* 111 | 112 | PHPで継続的インテグレーションを実践する方法はいろいろある。 113 | [Travis CI] のおかげで、 114 | ちょっとしたプロジェクトにも簡単に継続的インテグレーションを組み込めるようになった。 115 | Travis CIは継続的インテグレーションのホスティング環境で、オープンソースコミュニティに開放されている。 116 | GitHubと統合されており、PHPを含むさまざまな言語に対応している。 117 | GitHub は、継続的インテグレーションのワークフローとして [GitHub Actions][github_actions] を提供している。 118 | 119 | #### あわせて読みたい 120 | 121 | * [Jenkinsによる継続的インテグレーション][Jenkins] 122 | * [PHPCIによる継続的インテグレーション][PHPCI] 123 | * [PHP Censorによる継続的インテグレーション][PHP Censor] 124 | * [Teamcityによる継続的インテグレーション][Teamcity] 125 | 126 | [buildautomation]: https://wikipedia.org/wiki/Build_automation 127 | [Phing]: https://www.phing.info/ 128 | [Apache Ant]: https://ant.apache.org/ 129 | [Capistrano]: https://capistranorb.com/ 130 | [Ansistrano]: https://ansistrano.com 131 | [phpdeploy_deployer]: https://www.sitepoint.com/deploying-php-applications-with-deployer/ 132 | [Chef]: https://www.chef.io/ 133 | [chef_vagrant_and_ec2]: https://web.archive.org/web/20190307220000/http://www.jasongrimes.org/2012/06/managing-lamp-environments-with-chef-vagrant-and-ec2-1-of-3/ 134 | [Chef_cookbook]: https://github.com/sous-chefs/php 135 | [Chef_tutorial]: https://www.youtube.com/playlist?list=PL11cZfNdwNyNYcpntVe6js-prb80LBZuc 136 | [apache_ant_tutorial]: https://code.tutsplus.com/tutorials/automate-your-projects-with-apache-ant--net-18595 137 | [Travis CI]: https://www.travis-ci.com/ 138 | [Jenkins]: https://jenkins.io/ 139 | [PHPCI]: https://github.com/dancryer/phpci 140 | [PHP Censor]: https://github.com/php-censor/php-censor 141 | [Teamcity]: https://www.jetbrains.com/teamcity/ 142 | [Deployer]: https://deployer.org/ 143 | [Magallanes]: https://www.magephp.com/ 144 | [deploying_php_applications]: https://deployingphpapplications.com/ 145 | [Ansible]: https://www.ansible.com/ 146 | [Puppet]: https://puppet.com/ 147 | [ansible_for_devops]: https://leanpub.com/ansible-for-devops 148 | [ansible_for_aws]: https://leanpub.com/ansible-for-aws 149 | [an_ansible_tutorial]: https://serversforhackers.com/an-ansible-tutorial 150 | [github_actions]: https://docs.github.com/en/actions 151 | -------------------------------------------------------------------------------- /_posts/13-01-01-Virtualization.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 仮想化 3 | anchor: virtualization 4 | --- 5 | 6 | # 仮想化 {#virtualization_title} 7 | 8 | 開発環境だの本番環境だの、いろいろ違う環境でアプリケーションを動かしていると、おかしなバグに出くわしてしまいかねない。 9 | 「開発環境だと問題ないのに、本番環境だと動かない」みたいなやつだ。 10 | また、チームで開発しているときに、開発環境のいろんなライブラリのバージョンをきちんと統一しておくのも、面倒だ。 11 | 12 | Windowsで開発してLinux(などWindows以外の環境)にデプロイしていたり、チームで開発していたりする場合は、 13 | 仮想マシンを使うことを考えるべきだ。 14 | 何も難しいことはない。VMwareやVirtualBoxみたいな有名どころだけでなく、 15 | 仮想環境を簡単に準備するためのツールも用意されている。 16 | -------------------------------------------------------------------------------- /_posts/13-02-01-Vagrant.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | anchor: vagrant 4 | --- 5 | 6 | ## Vagrant {#vagrant_title} 7 | 8 | [Vagrant]を使えば、既知の仮想環境を使って自分用のボックスを作れて、その環境の構成も、たったひとつの設定ファイルだけでできる。 9 | このボックスを手動で設定することもできるし、 10 | [Puppet]とか[Chef]みたいな「プロビジョニング」ソフトにおまかせすることだってできる。 11 | ベースとなる環境を配布できるようにしておけば、複数の開発環境をまったく同じ状態に構築できる。 12 | 複雑怪奇なコマンドを羅列した「環境構築手順書」だとかいうのもいらなくなるってこと。 13 | ベース環境を「破棄」したり作りなおしたりするのもそんなに手間がかからないので、 14 | まっさらな環境を用意するのもお手軽にできる。 15 | 16 | Vagrantは、フォルダを作って、ホストと仮想マシンの間でコードを共有する。 17 | つまり、ホストマシンで作ったり編集したりしたコードを、そのまま仮想マシンの中で実行できるっていうことだ。 18 | 19 | [Vagrant]: https://www.vagrantup.com/ 20 | [Puppet]: https://puppet.com/ 21 | [Chef]: https://www.chef.io/ 22 | -------------------------------------------------------------------------------- /_posts/13-03-01-Docker.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docker 3 | isChild: true 4 | anchor: docker 5 | --- 6 | 7 | ## Docker {#docker_title} 8 | 9 | [Docker] - 完全仮想化されたマシンの軽量な代替 - は、すべてが "コンテナ" であるため、そのように呼ばれている。コンテナは、もっとも簡単なケースでは、特定のタスクをひとつだけ(たとえば Webサーバー)を実行するビルディングブロックだ。"イメージ" は、コンテナをビルドするために使うパッケージだ。Docker は、イメージを配布するためのリポジトリを持っている。 10 | 11 | 典型的な LAMP アプリケーションの場合、コンテナは3つ必要になる。Webサーバー、PHP-FPM プロセス、そして MySQL のコンテナだ。共有フォルダを Vagrant 内で使うのと同じように、アプリケーションのファイルはそのままで、Docker にどこを探せばよいのかを指示することができる。 12 | 13 | コンテナはコマンドライン (以下の例を見よう) から作成することもできるし、メンテナンスを簡単にするために `docker-compose.yml` ファイルをあなたのプロジェクト向けにビルドすることもできる。このファイルでは、何のコンテナを作るかを指定し、それらが互いにどう通信するのかを指示する。 14 | 15 | Docker は、複数の Web サイト を開発し、仮想マシン上でそれぞれの Webサイト のインストール環境を分離したい場合に役立つ。だが、すべてを最新の環境にしておくために、追加のディスクスペースや時間は必要ない。Docker は効率的だ: イメージのダウンロードやインストールは高速だし、何度イメージが必要になっても、保存しておく必要があるイメージのコピーはひとつだけだ。また、同じ OS のカーネルを共有するので、必要なメモリは少なくて済む。よって、多くのサーバを同時に実行できるし、停止や起動に掛かる時間も秒単位で済む。サーバマシンが完全に起動するのを待つ必要がないのだ。 16 | 17 | ### 例:PHPアプリケーションをDockerで実行する 18 | 19 | マシンに[Dockerをインストール][docker-install]したら、あと一手間だけで、PHP が使える Apache 環境を用意できる。 20 | 次のコマンドは、最新版のPHP入りのApache環境をダウンロードして、 21 | ディレクトリ `/path/to/your/php/files` を `http://localhost:8080` で見られるようにするものだ。 22 | 23 | {% highlight console %} 24 | docker run -d --name my-php-webserver -p 8080:80 -v /path/to/your/php/files:/var/www/html/ php:apache 25 | {% endhighlight %} 26 | 27 | これで、コンテナを初期化して実行できる。`-d` は、バックグラウンドで実行するオプションだ。 28 | コンテナを停止したり、再開したりしたくなったりした場合は、nameに指定した名前を使って 29 | `docker stop my-php-webserver` や `docker start my-php-webserver` などとするだけだ。 30 | さっきのパラメータを何度も指定する必要はない。 31 | 32 | ### Dockerについてもっと知りたい 33 | 34 | ここで紹介したのは、基本的なサーバーをお手軽に実行するためのコマンドだ。 35 | だけど、Docker にできることはまだまだたくさんある(そして、たくさんのビルド済みのイメージが [Docker Hub][docker-hub] にある)。Docker を最大限活用するために、[Docker ユーザーガイド][docker-doc] を読み、時間を掛けてそれらに関する用語を学んでいこう。そして、ダウンロードしたコードを安全かどうか確認せずに実行しないように注意しよう。非公式なイメージには最新のセキュリティパッチが当たっていない場合があるからだ。よくわからなければ、[オフィシャルのリポジトリ][docker-hub-official] を常に使うようにしよう。 36 | 37 | [PHPDocker.io] サイトは、あなたが選んだ PHP のバージョンや拡張機能を含めた形で、全ての機能を備えた LAMP/LEMP スタックに必要なファイルを全て自動生成してくれる。 38 | 39 | * [Docker 公式サイト][docker] 40 | * [Docker のインストール][docker-install] 41 | * [Docker ユーザーガイド][docker-doc] 42 | * [Docker Hub][docker-hub] 43 | * [Docker Hub - official images][docker-hub-official] 44 | 45 | [Docker]: https://www.docker.com/ 46 | [docker-hub]: https://hub.docker.com/ 47 | [docker-hub-official]: https://hub.docker.com/explore/ 48 | [docker-install]: https://docs.docker.com/get-docker/ 49 | [docker-doc]: https://docs.docker.com/ 50 | [PHPDocker.io]: https://phpdocker.io/ 51 | -------------------------------------------------------------------------------- /_posts/14-01-01-Caching.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: キャッシュ 3 | anchor: caching 4 | --- 5 | 6 | # キャッシュ {#caching_title} 7 | 8 | PHP 自体は極めて高速だけど、リモート接続やファイルの読み込みなどが絡むとボトルネックになるかもしれない。 9 | ありがたいことに、いろんなツールを活用すればアプリケーションを高速化できるし、 10 | 時間のかかる処理の実行回数を減らすこともできる。 11 | -------------------------------------------------------------------------------- /_posts/14-02-01-Opcode-Cache.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: オペコードキャッシュ 3 | isChild: true 4 | anchor: opcode_cache 5 | --- 6 | 7 | ## オペコードキャッシュ {#opcode_cache_title} 8 | 9 | PHPファイルを実行するときには、まずそれを[オペコード](https://php-legacy-docs.zend.com/manual/php4/en/internals2.opcodes) (CPU用の機械語の指示) 10 | にコンパイルしなければいけない。 11 | ソースコードに変更がなければ、オペコードも同じものになる。 12 | ということは、PHP ファイルに変更がなければコンパイル処理は CPU リソースの無駄遣いになるということだ。 13 | 14 | オペコードキャッシュは、コンパイル済みのオペコードをメモリに格納し、それ以降の呼び出しで再利用することで、 15 | 冗長なコンパイルを回避している。よくあるのは、ファイルのシグネチャや更新時刻をチェックして変更の有無を判断する方式だ。 16 | 17 | オペコードキャッシュを使えば、アプリケーションの実行速度が相当向上する可能性がある。 18 | PHP 5.5 からは、 [Zend OPcache][opcache-book] というオペコードキャッシュが標準で組み込まれるようになった。 19 | 使っている PHP パッケージやディストリビューションにもよるけど、普通はデフォルトで有効になっていることが多い。 20 | [opcache.enable](https://www.php.net/manual/opcache.configuration.php#ini.opcache.enable) 21 | や、 `phpinfo()` の出力で確認しよう。 22 | 古いバージョンのPHPなら、PECL の拡張モジュールが使える。 23 | 24 | オペコードキャッシュについて詳しく知りたければ、以下を参照すること。 25 | 26 | * [Zend OPcache][opcache-book] (PHP 5.5 以降に組み込まれている) 27 | * Zend OPcache (元 Zend Optimizer+) は [オープンソースになった][Zend Optimizer+] 28 | * [WinCache] (Microsoft Windows Server 用の拡張) 29 | * [Wikipediaにおける、PHPアクセラレータの一覧][PHP_accelerators] 30 | * [コードの事前ロード] - PHP >= 7.4 31 | 32 | 33 | [opcache-book]: https://www.php.net/book.opcache 34 | [Zend Optimizer+]: https://github.com/zendtech/ZendOptimizerPlus 35 | [WinCache]: https://www.iis.net/downloads/microsoft/wincache-extension 36 | [PHP_accelerators]: https://wikipedia.org/wiki/List_of_PHP_accelerators 37 | [コードの事前ロード]: https://www.php.net/opcache.preloading 38 | -------------------------------------------------------------------------------- /_posts/14-03-01-Object-Caching.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: オブジェクトキャッシュ 3 | isChild: true 4 | anchor: object_caching 5 | --- 6 | 7 | ## オブジェクトキャッシュ {#object_caching_title} 8 | 9 | コード内の個々のオブジェクトをキャッシュできれば便利なこともある。 10 | 持ってくるのにコストがかかるデータや、結果がほとんど変わらないデータベースへの問い合わせなどだ。 11 | オブジェクトキャッシュ用のソフトウェアを使えば、こういったデータをメモリ上に保持でき、 12 | その後のアクセスの高速化につながる。 13 | 一度取得したデータをどこかに格納しておいて、それ以降のリクエストではそこから直接取り出す。 14 | そうすればパフォーマンスは劇的に向上するし、データベースサーバーにかかる負荷も減らせる。 15 | 16 | バイトコードキャッシュ用ソリューションの多くはカスタムデータも同様にキャッシュできるので、さらに活用できる。 17 | APCu や WinCache は API を提供しており、PHP コードのデータをメモリキャッシュに格納できる。 18 | 19 | オブジェクトキャッシュシステムとして最もよく使われているのは APCu と memcached だ。 20 | APCu はオブジェクトキャッシュの選択肢として最適で、 21 | シンプルな API を使ってデータをメモリキャッシュに格納できる。 22 | セットアップも簡単で、すぐに使えるようになる。 23 | APCu の制約のひとつは、インストールしたサーバーと密接に結合してしまうことだ。 24 | 一方、Memcached は個別のサービスとしてインストールするものであり、 25 | ネットワーク越しにアクセスできる。つまり、 26 | 中央で管理している超高速なストレージにオブジェクトを格納して、 27 | いろんなシステムからそれを取り出せるということだ。 28 | 29 | PHPを(Fast-)CGIアプリケーションとしてウェブサーバーで実行するときには、 30 | すべてのPHPプロセスが自身のキャッシュを持つことになる。つまり 31 | APCuのデータもワーカープロセス間では共有されないということだ。 32 | こんなときには、かわりにmemcachedを検討すればいい。 33 | こっちはPHPのプロセスには結びついていない。 34 | 35 | ネットワーク環境において、アクセス速度の面では APCu のほうが memcached より優れている。 36 | しかし、memcached のほうが、より手軽にスケールアップできる。 37 | 複数のサーバーを使う予定がないとか memcached の追加機能が不要だという場合は、 38 | オブジェクトキャッシュに APCu を選ぶのが最適だろう。 39 | 40 | APCu を使うロジックの例を示す。 41 | 42 | {% highlight php %} 43 | 19 | * @link https://www.phpdoc.org/docs/latest/index.html 20 | */ 21 | class DateTimeHelper 22 | { 23 | /** 24 | * @param mixed $anything Anything that we can convert to a \DateTime object 25 | * 26 | * @throws \InvalidArgumentException 27 | * 28 | * @return \DateTime 29 | */ 30 | public function dateTimeFromAnything($anything) 31 | { 32 | $type = gettype($anything); 33 | 34 | switch ($type) { 35 | // Some code that tries to return a \DateTime object 36 | } 37 | 38 | throw new \InvalidArgumentException( 39 | "Failed Converting param of type '{$type}' to DateTime object" 40 | ); 41 | } 42 | 43 | /** 44 | * @param mixed $date Anything that we can convert to a \DateTime object 45 | * 46 | * @return void 47 | */ 48 | public function printISO8601Date($date) 49 | { 50 | echo $this->dateTimeFromAnything($date)->format('c'); 51 | } 52 | 53 | /** 54 | * @param mixed $date Anything that we can convert to a \DateTime object 55 | */ 56 | public function printRFC2822Date($date) 57 | { 58 | echo $this->dateTimeFromAnything($date)->format('r'); 59 | } 60 | } 61 | {% endhighlight %} 62 | 63 | クラス全体のドキュメントの最初にあるのが [@author] タグと [@link] タグだ。 64 | [@author] タグは、コードの作者を表す。作者が複数いる場合は、何度も繰り返し使ってもかまわない。 65 | [@link] タグは、そのコードに関連するウェブサイトへのリンクを示すために使う。 66 | 67 | クラスの中に目を移すと、最初のメソッドには [@param] タグが記されている。 68 | これは、このメソッドに渡すパラメータの型と名前そして説明を記述するものだ。 69 | さらに、[@return] タグと 70 | [@throws] タグも書かれている。 71 | これらはそれぞれ、戻り値の型と、発生する可能性のある例外を記述するものだ。 72 | 73 | 二番目と三番目のメソッドはほぼ同じだ。まず、最初のメソッドと同様に [@param] タグが書かれている。 74 | 二番目と三番目のメソッドの大きな違いは、 [@return] タグの有無だ。 75 | `@return void` は、このメソッドが何も戻さないことを明示している。 76 | 一方、歴史的に、 `@return void` を省略した場合も同じ意味(何も値を戻さない)になる。 77 | 78 | 79 | [tags]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/index.html 80 | [PHPDoc のマニュアル]: https://docs.phpdoc.org/latest/index.html 81 | [@author]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/author.html 82 | [@link]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/link.html 83 | [@param]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/param.html 84 | [@return]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/return.html 85 | [@throws]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/throws.html 86 | -------------------------------------------------------------------------------- /_posts/16-01-01-Resources.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 情報源 3 | anchor: resources 4 | --- 5 | 6 | # 情報源 {#resources_title} 7 | -------------------------------------------------------------------------------- /_posts/16-02-01-From-the-Source.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ソースから 3 | isChild: true 4 | anchor: from_the_source 5 | --- 6 | 7 | ## ソースから {#from_the_source_title} 8 | 9 | * [PHP のサイト](https://www.php.net/) 10 | * [PHP のドキュメント](https://www.php.net/docs.php) 11 | -------------------------------------------------------------------------------- /_posts/16-03-01-People-to-Follow.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: フォローすべき人たち 3 | isChild: true 4 | anchor: people_to_follow 5 | --- 6 | 7 | ## フォローすべき人たち {#people_to_follow_title} 8 | 9 | PHPコミュニティの中で注目すべき人を見つけるのは、コミュニティに加わったばかりの人たちにとっては難しいものだ。 10 | PHPコミュニティの主要メンバーの一覧が、ここにまとまっている。 11 | 12 | * 13 | * 14 | -------------------------------------------------------------------------------- /_posts/16-04-01-Mentoring.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: メンタリング 3 | isChild: true 4 | anchor: mentoring 5 | --- 6 | 7 | ## メンタリング {#mentoring_title} 8 | 9 | * [php-mentoring.org](https://php-mentoring.org/) - Formal, peer to peer mentoring in the PHP community. 10 | -------------------------------------------------------------------------------- /_posts/16-05-01-PHP-PaaS-Providers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PHP PaaS プロバイダ 3 | isChild: true 4 | anchor: php_paas_providers 5 | --- 6 | 7 | ## PHP PaaS プロバイダ {#php_paas_providers_title} 8 | 9 | * [AppFog](https://www.ctl.io/appfog/) 10 | * [Amezmo](https://www.amezmo.com) 11 | * [AWS Elastic Beanstalk](https://aws.amazon.com/elasticbeanstalk/) 12 | * [Cloudways](https://www.cloudways.com/) 13 | * [Divio](https://www.divio.com/php/) 14 | * [Engine Yard Cloud](https://www.engineyard.com/) 15 | * [fortrabbit](https://www.fortrabbit.com/) 16 | * [Google App Engine](https://cloud.google.com/appengine/docs/php/) 17 | * [Heroku](https://devcenter.heroku.com/categories/php-support) 18 | * [IBM Cloud](https://console.bluemix.net/docs/runtimes/php/getting-started.html#getting_started) 19 | * [Jelastic](https://jelastic.com/) 20 | * [Microsoft Azure](https://azure.microsoft.com/) 21 | * [Nanobox](https://nanobox.io/) 22 | * [Pivotal Web Services](https://run.pivotal.io/) 23 | * [Platform.sh](https://platform.sh/) 24 | * [Red Hat OpenShift](https://www.openshift.com/) 25 | -------------------------------------------------------------------------------- /_posts/16-06-01-Frameworks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: フレームワーク 3 | isChild: true 4 | anchor: frameworks 5 | --- 6 | 7 | # フレームワーク {#frameworks_title} 8 | 9 | PHP 開発者の多くは、ウェブアプリケーションを作るときに車輪の再発明を避けてフレームワークを使っている。 10 | フレームワークを使えば低レベルの作業の多くを抽象化でき、 11 | 便利で使いやすいインターフェイスでよくある作業をこなせる。 12 | 13 | 別に、フレームワークを使わないといけないというわけじゃない。 14 | ふつうに PHP で書くべき場面だってある。 15 | もしフレームワークを使うのなら、フレームワークはこんな感じに分類できることを知っておこう。 16 | 17 | * マイクロフレームワーク 18 | * フルスタックフレームワーク 19 | * コンポーネントフレームワーク 20 | 21 | マイクロフレームワークというのは本質的にはラッパーで、 22 | HTTP リクエストを手っ取り早くコールバックに振り分けるだけのものだ。 23 | 場合によっては、開発を支援するためのちょっとした追加ライブラリが付属することもある。 24 | データベースの基本的なラッパーなどだ。マイクロフレームワークの主な使い道は、 25 | リモート HTTP サービスの構築である。 26 | 27 | 多くのフレームワークは、マイクロフレームワークが持つ機能に加えて大量の機能を用意している。 28 | この種のフレームワークのことを、フルスタックフレームワークと呼ぶ。 29 | ORM や認証パッケージなどが含まれることが多い。 30 | 31 | コンポーネントフレームワークとは、特定の目的のための専用ライブラリをとりまとめたフレームワークである。 32 | この種のフレームワークのコンポーネントを各種組み合わせて、 33 | マイクロフレームワークやフルスタックフレームワークを作ることもできる。 34 | -------------------------------------------------------------------------------- /_posts/16-07-01-Components.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: コンポーネント 3 | isChild: true 4 | anchor: components 5 | --- 6 | 7 | ## コンポーネント {#components_title} 8 | 9 | 先ほど説明したように、「コンポーネント」っていうのは 10 | 共有するコードを作ったりそれを配布したりするための手段のひとつだ。 11 | コンポーネントを登録するリポジトリにもいろいろあるけど、中でも有名なのがこのふたつだ。 12 | 13 | * [Packagist] 14 | * [PEAR] 15 | 16 | どちらのリポジトリについてもコマンドラインのツールが存在し、 17 | コンポーネントのインストールやアップグレードを簡単にできる。 18 | 詳細は[依存関係の管理][dm]を参照すること。 19 | 20 | コンポーネントベースのフレームワークもあれば、フレームワークを持たずにコンポーネントだけを提供するベンダーもある。 21 | こういったプロジェクトが提供するパッケージは、他のパッケージや特定のフレームワークへの依存がほとんどない。 22 | 23 | たとえば[FuelPHPのValidationパッケージ][fuelval]を使うときには、 24 | 別にFuelPHPフレームワークを使う必要はない。 25 | 26 | * [Aura] 27 | * CakePHP Components 28 | * [Collection] 29 | * [Database] 30 | * [Datasource] 31 | * [Event] 32 | * [I18n] 33 | * [ORM] 34 | * [FuelPHP] 35 | * [Hoa Project] 36 | * [Symfony Components] 37 | * [The League of Extraordinary Packages] 38 | * Laravel's Illuminate Components 39 | * [IoC Container] 40 | * [Eloquent ORM] 41 | * [Queue] 42 | 43 | _Laravelの [Illuminate コンポーネント] も、将来的には Laravel フレームワークからきちんと切り離されるようになるだろう。 44 | 現段階では、Laravel フレームワークからうまく切り離せているコンポーネントだけをリストにあげておいた。_ 45 | 46 | 47 | [Packagist]: /#composer_と_packagist 48 | [PEAR]: /#pear 49 | [Dependency Management]: /#依存関係の管理 50 | [FuelPHP Validation package]: https://github.com/fuelphp/validation 51 | [Aura]: https://auraphp.com/framework/ 52 | [FuelPHP]: https://github.com/fuelphp 53 | [Hoa Project]: https://github.com/hoaproject 54 | [Symfony Components]: https://symfony.com/components 55 | [The League of Extraordinary Packages]: https://thephpleague.com/ 56 | [IoC Container]: https://github.com/illuminate/container 57 | [Eloquent ORM]: https://github.com/illuminate/database 58 | [Queue]: https://github.com/illuminate/queue 59 | [Illuminate コンポーネント]: https://github.com/illuminate 60 | [Collection]: https://github.com/cakephp/collection 61 | [Database]: https://github.com/cakephp/database 62 | [Datasource]: https://github.com/cakephp/datasource 63 | [Event]: https://github.com/cakephp/event 64 | [I18n]: https://github.com/cakephp/i18n 65 | [ORM]: https://github.com/cakephp/orm 66 | -------------------------------------------------------------------------------- /_posts/16-08-01-Sites.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | anchor: other_resources 4 | title: その他の資料 5 | --- 6 | 7 | ## その他の資料 {#other_resources_title} 8 | 9 | ### チートシート 10 | 11 | * [PHP Cheatsheets](https://phpcheatsheets.com/) - さまざまなバージョンの PHP における、変数の比較や演算など 12 | * [Modern PHP Cheatsheet](https://github.com/smknstd/modern-php-cheatsheet) - モダン(PHP 7.0+) なイディオムが、ひとつの文書にまとまっている 13 | * [OWASP Security Cheatsheets](https://owasp.org/www-project-cheat-sheets/) - セキュリティに関するさまざまなトピックの有用な情報を簡潔にまとめたもの 14 | 15 | ### ベストプラクティス 16 | 17 | * [PHP Best Practices](https://phpbestpractices.org/) 18 | * [Why You Should Be Using Supported PHP Versions](https://kinsta.com/blog/php-versions/) 19 | 20 | ### PHPやウェブ開発コミュニティ界隈のニュース 21 | 22 | ニュースレターを購読すれば、新しいライブラリや最新ニュースやイベントなどの通知を受け取れる。 23 | それだけじゃなくて、新旧さまざまな記事も紹介してくれるだろう。 24 | 25 | * [PHP Weekly](https://www.phpweekly.com) 26 | * [JavaScript Weekly](https://javascriptweekly.com/) 27 | * [Frontend Focus](https://frontendfoc.us/) 28 | * [Mobile Web Weekly](https://mobiledevweekly.com/) 29 | 30 | その他のプラットフォームにも同じような週刊ニュースレターがある。 [その一部をまとめた](https://github.com/jondot/awesome-weekly)。 31 | 32 | ### PHP界 33 | 34 | * [PHP Developer blog](https://blog.phpdeveloper.org/) 35 | -------------------------------------------------------------------------------- /_posts/16-09-01-Videos.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | anchor: videos 4 | title: 動画チュートリアル 5 | --- 6 | 7 | ## 動画チュートリアル {#videos} 8 | 9 | ### YouTubeチャンネル 10 | 11 | * [Learn PHP The Right Way Series](https://github.com/ggelashvili/learnphptherightway-outline) 12 | * [PHP Academy](https://www.youtube.com/user/phpacademy) 13 | * [The New Boston](https://www.youtube.com/user/thenewboston) 14 | * [Sherif Ramadan](https://www.youtube.com/user/businessgeek) 15 | * [Level Up Tuts](https://www.youtube.com/user/LevelUpTuts) 16 | 17 | ### 有償の動画 18 | 19 | * [Standards and Best practices](https://teamtreehouse.com/library/php-standards-and-best-practices) 20 | * [PHP Training on Pluralsight](https://www.pluralsight.com/search?q=php) 21 | * [PHP Training on LinkedIn.com](https://www.linkedin.com/learning/search?trk=lynda_redirect_learning&sortBy=RELEVANCE&softwareNames=PHP) 22 | * [PHP Training on Tutsplus](https://code.tutsplus.com/categories/php/courses) 23 | * [Laracasts](https://laracasts.com/) 24 | * [SymfonyCasts](https://symfonycasts.com/) 25 | -------------------------------------------------------------------------------- /_posts/16-10-01-Books.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 書籍 3 | isChild: true 4 | anchor: books 5 | --- 6 | 7 | ## 書籍 {#books_title} 8 | 9 | PHPに関する本はいっぱい出てるけど、絶望的に古くなってしまっていてもはや正しい情報ではなくなっているものもある。 10 | なぜか、この世に存在すらしていない「PHP 6」向けの本まで出ていたりする。 11 | これが、PHP 5.6の次のメジャーバージョンの名前が「PHP 7」に決まった[理由のひとつかもしれない](https://wiki.php.net/rfc/php6)。 12 | 13 | このセクションでは、PHPでの開発全般でおすすめの書籍を紹介する。 14 | 自分の本を載せて欲しいという人は、プルリクエストを出してもらえばレビューのうえで追加する。 15 | 16 | ### 無償の書籍 17 | 18 | * [PHP Pandas](https://daylerees.com/php-pandas/) - ウェブ開発者になるための方法を教えるのが狙いだ。 19 | * [PHP The Right Way](https://leanpub.com/phptherightway/) - このウェブサイトの内容が、無料の書籍として公開されている。 20 | * [Using Libsodium in PHP Projects](https://paragonie.com/book/pecl-libsodium) - Libsodium 拡張モジュールを使って、 21 | モダンでセキュアかつ高速な暗号化を行うためのガイド。 22 | 23 | ### 有償の書籍 24 | 25 | * [PHP & MySQL](https://phpandmysql.com/) - PHP book with excellent illustrations that covers all the fundamentals of PHP and MySQL with practical examples. 26 | * [Build APIs You Won't Hate](https://apisyouwonthate.com/) - Everyone and their dog wants an API, 27 | so you should probably learn how to build them. 28 | * [Modern PHP](https://www.oreilly.com/library/view/modern-php/9781491905173/) - Covers modern PHP features, best practices, testing, tuning, deployment and setting up a dev environment. 29 | * [Building Secure PHP Apps](https://leanpub.com/buildingsecurephpapps) - Learn the security basics that a senior 30 | developer usually acquires over years of experience, all condensed down into one quick and easy handbook. 31 | * [Modernizing Legacy Applications In PHP](https://leanpub.com/mlaphp) - 細かく順を追って、自分のコードを手なずけていく。 32 | * [Securing PHP: Core Concepts](https://leanpub.com/securingphp-coreconcepts) - A guide to some of the most common 33 | security terms and provides some examples of them in every day PHP. 34 | * [Scaling PHP](https://www.scalingphpbook.com/) - Stop playing sysadmin and get back to coding. 35 | * [Signaling PHP](https://leanpub.com/signalingphp) - PCNLT signals are a great help when writing PHP scripts that 36 | run from the command line. 37 | * [Minimum Viable Tests](https://leanpub.com/minimumviabletests) - Long-time PHP testing evangelist Chris Hartjes goes over what he feels is the minimum you need to know to get started. 38 | * [Domain-Driven Design in PHP](https://leanpub.com/ddd-in-php) - See real examples written in PHP showcasing Domain-Driven Design Architectural Styles (Hexagonal Architecture, CQRS or Event Sourcing), Tactical Design Patterns, and Bounded Context Integration. 39 | -------------------------------------------------------------------------------- /_posts/17-01-01-Community.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: コミュニティ 3 | anchor: community 4 | --- 5 | 6 | # コミュニティ {#community_title} 7 | 8 | PHP のコミュニティは、規模が大きくなるにつれて多様化しており、 9 | どこのコミュニティでも新たな仲間を歓迎している。 10 | 近所のユーザーグループに参加したり、ちょっと大きめのカンファレンスに参加したりすれば、 11 | このページで紹介したことよりもずっと多くを学べるだろう。 12 | IRC なら [irc.freenode.com][php-irc] に #phpc というチャンネルがあるし、 13 | [@phpc][phpc-twitter] や [Mastodon][phpc-mastodon] をフォローするといい。 14 | いろんなところに飛び込んで、新しい人と出会って、いろんなことを学んで、友達になるんだ。 15 | [StackOverflow][php-so] にもPHPプログラマー向けのコミュニティがある。 16 | 17 | [PHP 公式サイトのイベントカレンダー][php-calendar] 18 | 19 | 20 | [php-irc]: https://webchat.freenode.net/?channels=phpc 21 | [phpc-twitter]: https://twitter.com/phpc 22 | [phpc-mastodon]: https://phpc.social/ 23 | [php-so]: https://stackoverflow.com/questions/tagged/php 24 | [php-calendar]: https://www.php.net/cal.php 25 | -------------------------------------------------------------------------------- /_posts/17-02-01-User-Groups.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PHP ユーザーグループ 3 | isChild: true 4 | anchor: user_groups 5 | --- 6 | 7 | ## PHP ユーザーグループ {#user_groups_title} 8 | 9 | 大都市に住んでいるなら、きっと近場に PHP ユーザーグループがあるはずだ。 10 | 近場の PHP ユーザーグループを見つけるには、[PHP.ug][php-ug] を使えばいい。 11 | それ以外にも、[Meetup.com][meetup] を使ってもいいし、 12 | お好みのサーチエンジン (たとえば [Google][google] とか) で 13 | ```php user group near me``` みたいに検索してみるとかね。 14 | 小さな町なら、もしかしたらユーザーグループがないかもしれない。 15 | だったら、作ればいい! 16 | 17 | 数々のグループの中でも、特筆すべきものがある。[NomadPHP] と [PHPWomen] だ。 18 | [NomadPHP] は、オンラインでのミーティングを毎月2回開催している。 19 | そのミーティングでは、PHP コミュニティの有名人によるプレゼンも行われている。 20 | [PHPWomen] は、PHP 界の女性をターゲットにしたユーザーグループだったが、今はそれにとどまらない。 21 | より多様性のあるコミュニティを目指そうという人なら、誰でも受け入れている。 22 | PHPWomen は、サポートやメンターシップ、教育などのネットワークを提供している。 23 | そして「女性にやさしい」プロフェッショナルな空気を広めることを目指している。 24 | 25 | [PHP Wiki のユーザーグループ情報][php-wiki] 26 | 27 | [google]: https://www.google.com/search?q=php+user+group+near+me 28 | [meetup]: https://www.meetup.com/find/ 29 | [php-ug]: https://php.ug/ 30 | [NomadPHP]: https://nomadphp.com/ 31 | [PHPWomen]: https://twitter.com/PHPWomen 32 | [php-wiki]: https://wiki.php.net/usergroups 33 | -------------------------------------------------------------------------------- /_posts/17-03-01-Conferences.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PHP カンファレンス 3 | isChild: true 4 | anchor: conferences 5 | --- 6 | 7 | ## PHP カンファレンス {#conferences_title} 8 | 9 | 世界各国で、地域レベルあるいは国レベルの大規模なカンファレンスが開かれている。 10 | PHP界の有名人が登壇することも多いので、彼らから直接学ぶチャンスだ。 11 | 12 | [PHP のカンファレンスを探す][php-conf] 13 | 14 | 15 | [php-conf]: https://www.php.net/conferences/index.php 16 | -------------------------------------------------------------------------------- /_posts/17-04-01-Elephpants.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ElePHPants 3 | isChild: true 4 | anchor: elephpants 5 | --- 6 | 7 | ## ElePHPants {#elephpants_title} 8 | 9 | [ElePHPant][elephpant] は PHP プロジェクトのマスコットだ。 10 | もともとは 1998 年に [Vincent Pontier][vincent-pontier] がデザインしたもの。つまり彼は、世界中の elePHPant たちの父なる神である。 11 | その約10年後には、かわいらしいぬいぐるみもできあがった。 12 | いまやあちこちのPHPカンファレンスでelePHPantが見られるし、自分のPCのそばにelePHPantを置いているPHP開発者も多い。 13 | 14 | [Vincent Pontierへのインタビュー][vincent-pontier-interview] 15 | 16 | 17 | [elephpant]: https://www.php.net/elephpant.php 18 | [vincent-pontier-interview]: https://7php.com/elephpant/ 19 | [vincent-pontier]: http://www.elroubio.net/ 20 | -------------------------------------------------------------------------------- /banners.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Website Banners 4 | description: "Spread the word! Use these banner to let new PHP programmers know about PHP: The Right Way" 5 | sitemap: true 6 | --- 7 | 8 | # ウェブ用のバナー 9 | 10 | このバナー画像を使って _PHP: The Right Way_ を広めよう! 11 | PHPを勉強したい人たちに「いい情報がここにあるよ!」ってぜひ伝えるんだ。 12 | 13 | ## ボタン 1 (120x90) 14 | 15 |

    PHP: The Right Way

    16 | 17 | {% highlight html %} 18 | 19 | PHP: The Right Way 20 | 21 | {% endhighlight %} 22 | 23 | ## ボタン 2 (120x60) 24 | 25 |

    PHP: The Right Way

    26 | 27 | {% highlight html %} 28 | 29 | PHP: The Right Way 30 | 31 | {% endhighlight %} 32 | 33 | ## ビッグバナー (728x90) 34 | 35 |

    PHP: The Right Way

    36 | 37 | {% highlight html %} 38 | 39 | PHP: The Right Way 40 | 41 | {% endhighlight %} 42 | 43 | ## 大型長方形 (386x280) 44 | 45 |

    PHP: The Right Way

    46 | 47 | {% highlight html %} 48 | 49 | PHP: The Right Way 50 | 51 | {% endhighlight %} 52 | 53 | ## 中型長方形 (300x250) 54 | 55 |

    PHP: The Right Way

    56 | 57 | {% highlight html %} 58 | 59 | PHP: The Right Way 60 | 61 | {% endhighlight %} 62 | 63 | ## 長方形 (180x150) 64 | 65 |

    PHP: The Right Way

    66 | 67 | {% highlight html %} 68 | 69 | PHP: The Right Way 70 | 71 | {% endhighlight %} 72 | 73 | ## 正方形 (125x125) 74 | 75 |

    PHP: The Right Way

    76 | 77 | {% highlight html %} 78 | 79 | PHP: The Right Way 80 | 81 | {% endhighlight %} 82 | 83 | ## 縦長長方形 (240x400) 84 | 85 |

    PHP: The Right Way

    86 | 87 | {% highlight html %} 88 | 89 | PHP: The Right Way 90 | 91 | {% endhighlight %} 92 | -------------------------------------------------------------------------------- /images/banners/btn1-120x90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/btn1-120x90.png -------------------------------------------------------------------------------- /images/banners/btn2-120x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/btn2-120x60.png -------------------------------------------------------------------------------- /images/banners/leaderboard-728x90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/leaderboard-728x90.png -------------------------------------------------------------------------------- /images/banners/lg-rect-386x280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/lg-rect-386x280.png -------------------------------------------------------------------------------- /images/banners/med-rect-300x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/med-rect-300x250.png -------------------------------------------------------------------------------- /images/banners/rect-180x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/rect-180x150.png -------------------------------------------------------------------------------- /images/banners/sq-btn-125x125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/sq-btn-125x125.png -------------------------------------------------------------------------------- /images/banners/vert-rect-240x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/vert-rect-240x400.png -------------------------------------------------------------------------------- /images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/favicon.png -------------------------------------------------------------------------------- /images/nmc-logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/nmc-logo.gif -------------------------------------------------------------------------------- /images/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/og-image.png -------------------------------------------------------------------------------- /images/og-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/og-logo.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | description: "An easy-to-read, quick reference for PHP best practices, accepted coding standards, and links to authoritative PHP tutorials around the Web" 4 | sitemap: true 5 | --- 6 | 7 | {% capture welcome_content %}{% include welcome.md %}{% endcapture %} 8 |
    9 | {{ welcome_content|markdownify }} 10 |
    11 | 12 | {% capture backtotop %}[Back to Top](#top){:.top}{% endcapture %} 13 | {% for post in site.posts reversed %} 14 | {% if post.isChild != true and loop.first != true %}
    {{ backtotop|markdownify }}
    {% endif %} 15 |
    16 | {{ post.content }} 17 |
    18 | {% endfor %} 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-the-right-way", 3 | "version": "2.0.0", 4 | "devDependencies": { 5 | "grunt": "~0.4.5", 6 | "grunt-contrib-less": "~1.0.1", 7 | "grunt-contrib-watch": "~0.6.1", 8 | "grunt-postcss": "^0.6.0", 9 | "autoprefixer": "^6.0.3" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /pages/Design-Patterns.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Design Patterns 4 | sitemap: true 5 | --- 6 | 7 | # デザインパターン 8 | 9 | ウェブアプリケーションのコードやプロジェクトを作っていくにはいろんなやりかたがあって、 10 | どんなふうに作るか考え抜くのもありだし適当に作るのもありだ。 11 | でも普通は、一般的なパターンに従うほうがいい。そのほうがコードを管理しやすいし、 12 | 他の人にもそのコードを理解してもらいやすくなるからである。 13 | 14 | * [Architectural pattern (Wikipedia)](https://en.wikipedia.org/wiki/Architectural_pattern) 15 | * [デザインパターン (Wikipedia)](https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3_(%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2)) 16 | * [実装サンプル集](https://designpatternsphp.readthedocs.io/en/latest/) 17 | 18 | ## ファクトリー 19 | 20 | 最も多用されているデザインパターンのひとつが、ファクトリーパターンだ。このパターンでは、 21 | 使いたいオブジェクトを生成するだけのクラスを用意する。ファクトリーパターンの例として、 22 | こんなコードを考えてみよう。 23 | 24 | {% highlight php %} 25 | vehicleMake = $make; 34 | $this->vehicleModel = $model; 35 | } 36 | 37 | public function getMakeAndModel() 38 | { 39 | return $this->vehicleMake . ' ' . $this->vehicleModel; 40 | } 41 | } 42 | 43 | class AutomobileFactory 44 | { 45 | public static function create($make, $model) 46 | { 47 | return new Automobile($make, $model); 48 | } 49 | } 50 | 51 | // ファクトリーを使って Automobile オブジェクトを作る 52 | $veyron = AutomobileFactory::create('ブガッティ', 'ヴェイロン'); 53 | 54 | print_r($veyron->getMakeAndModel()); // 出力は "ブガッティ ヴェイロン" 55 | {% endhighlight %} 56 | 57 | このコードは、ファクトリーパターンを使って Automobile オブジェクトを作る。 58 | こんなふうにするメリットは二つある。 59 | まず、もし後で Automobile クラスに手を入れたり名前を変更したり別のものに入れ替えたりすることになっても簡単にできるということ。 60 | 単にファクトリーの中のコードを変更すれば済むわけで、 61 | コードの中で Automobile クラスを使っているところをひとつひとつ修正するとかいうことはしなくてもよい。 62 | 二番目のメリットは、仮にオブジェクトの生成が複雑な作業になってしまっても、 63 | そのすべてのファクトリーに閉じ込めてしまえること。 64 | 新しいインスタンスを作るたびに毎回同じようなことを繰り返さずに済む。 65 | 66 | なにがなんでもファクトリーパターンを使えばいいってわけではない。 67 | この例のコードはとてもシンプルだし、この程度だとファクトリーパターンのおかげで 68 | 無駄に複雑になったように見えるかもしれない。 69 | でも、それなりに大規模で込み入ったプロジェクトにかかわる場合は、 70 | 変なトラブルに巻き込まれないためにもファクトリーを使っておいたほうがいいだろう。 71 | 72 | * [ファクトリーパターン (Wikipedia)](https://ja.wikipedia.org/wiki/Factory_Method_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3) 73 | 74 | ## シングルトン 75 | 76 | ウェブアプリケーションを設計するときには、概念的そして構造的に、 77 | 特定のクラスのたったひとつのインスタンスにだけアクセスさせるようにしたいということがよくある。 78 | そんなときに使えるのがシングルトンパターンだ。 79 | 80 | **TODO: NEED NEW SINGLETON CODE EXAMPLE** 81 | 82 | このコードは、[*静的* な変数](https://www.php.net/language.variables.scope#language.variables.scope.static) 83 | と`getInstance()`メソッドを使ってシングルトンパターンを実装している。 84 | これらのことに注目しよう。 85 | 86 | * コンストラクタ [`__construct`](https://www.php.net/language.oop5.decon#object.construct) が protected 宣言されている。これで、このクラスの外部からは `new` 演算子で新しいインスタンスを作れなくなる。 87 | * マジックメソッド [`__clone`](https://www.php.net/language.oop5.cloning#object.clone) が private 宣言されている。これで、[`clone`](http://php.net/language.oop5.cloning) 演算子でインスタンスをクローンしようとしてもできなくなる。 88 | * マジックメソッド [`__wakeup`](https://www.php.net/language.oop5.magic#object.wakeup) が private 宣言されている。これで、グローバル関数 [`unserialize()`](https://www.php.net/function.unserialize) でのインスタンスのアンシリアライズができなくなる。 89 | * 新しいインスタンスを作るには、静的な作成用メソッド `getInstance()` による [遅延静的束縛](https://www.php.net/language.oop5.late-static-bindings) を使う。このメソッドの中ではキーワード `static` が使われていて、サンプルのように `Singleton` のサブクラスを作れるようになる。 90 | 91 | シングルトンパターンが有用なのは、たとえばウェブアプリケーションのリクエスト全体で、 92 | たったひとつのインスタンスだけしかないことを保証しないといけない場合だ。 93 | Configurationクラスみたいなグローバルオブジェクトを使っていたり、 94 | イベントキューみたいな共有リソースを使っていたりする場合に活用できる。 95 | 96 | ただ、シングルトンを使うときには注意が必要だ。その性質上、シングルトンパターンを使うと 97 | アプリケーションにグローバルな状態を導入することになってしまい、テストがしにくくなる。 98 | シングルトンを使いたいという場面では、たいていの場合は依存性の注入が代わりに使える 99 | (し、むしろそっちを使うべきだ)。依存性の注入を使えば、不要な結合を 100 | アプリケーションの設計から取り除ける。というのも、共有リソースやグローバルリソースを使う 101 | オブジェクトが具象クラスについて知らなくてもよくなるからだ。 102 | 103 | * [シングルトンパターン (Wikipedia)](https://en.wikipedia.org/wiki/Singleton_pattern) 104 | 105 | ## ストラテジー 106 | 107 | ストラテジーパターンを使うと、一連のアルゴリズム群をカプセル化して、 108 | クライアントクラス側から特定のアルゴリズムのインスタンスを作れるようになる。 109 | このときに、実際の実装に関する知識はなくてもかまわない。 110 | このパターンにはいくつかのバリエーションがあるけれど、ここでは一番シンプルなものを解説する。 111 | 112 | 最初に示すのは、アルゴリズム群を表すコード片だ。 113 | シリアライズした配列、JSON、あるいは単なるデータの配列が欲しいとしよう。 114 | {% highlight php %} 115 | output = $outputType; 168 | } 169 | 170 | public function loadOutput() 171 | { 172 | return $this->output->load(); 173 | } 174 | } 175 | {% endhighlight %} 176 | 177 | このクライアントクラスにはprivateなプロパティが用意されている。 178 | 実行時には、必ず'OutputInterface'型を設定しておく必要がある。 179 | このプロパティを設定すれば、loadOutput()を呼んだときに、 180 | 指定した型の具象クラスのload()メソッドが呼ばれることになる。 181 | {% highlight php %} 182 | setOutput(new ArrayOutput()); 187 | $data = $client->loadOutput(); 188 | 189 | // JSONが使いたければ 190 | $client->setOutput(new JsonStringOutput()); 191 | $data = $client->loadOutput(); 192 | 193 | {% endhighlight %} 194 | 195 | * [ストラテジーパターン (Wikipedia)](https://en.wikipedia.org/wiki/Strategy_pattern) 196 | 197 | ## フロントコントローラ 198 | 199 | フロントコントローラパターンは、ウェブアプリケーションのエントリポイントをひとつだけ (例: index.php) にして 200 | そこですべてのリクエストを処理するというパターンだ。このエントリポイントが、 201 | 依存情報を読み込んだりリクエストを処理したりレスポンスをブラウザに返したりといった責務を負う。 202 | フロントコントローラを利用すると、コードのモジュール化が進めやすくなる。 203 | また、すべてのリクエストに対して実行したいコード 204 | (入力のチェックなど) をフックとして組み込みやすくなる。 205 | 206 | * [フロントコントローラパターン (Wikipedia)](https://en.wikipedia.org/wiki/Front_Controller_pattern) 207 | 208 | ## Model-View-Controller 209 | 210 | モデル=ビュー=コントローラ (MVC) パターン、そしてその関連パターンである HMVC や MVVM 211 | を使うと、コードを論理的に分解してそれぞれ特定の役割を担わせることができる。 212 | モデルはデータアクセス層を扱い、データを取得してそれをアプリケーションで使いやすい形式で返す。 213 | コントローラはリクエストを扱い、モデルから受け取ったデータを処理してビューを読み込み、 214 | それをレスポンスとして返す。 215 | ビューはテンプレート (マークアップや xml など) を扱い、これをレスポンスとしてウェブブラウザに返す。 216 | 217 | MVC は最も一般的なアーキテクチャパターンで、主要な [PHP フレームワーク](https://github.com/codeguy/php-the-right-way/wiki/Frameworks) 218 | でも採用されている。 219 | 220 | MVC やその関連パターンについてさらに知りたければ、これらを参考にしよう。 221 | 222 | * [MVC](https://ja.wikipedia.org/wiki/Model_View_Controller) 223 | * [HMVC](https://en.wikipedia.org/wiki/Hierarchical_model%E2%80%93view%E2%80%93controller) 224 | * [MVVM](https://ja.wikipedia.org/wiki/Model_View_ViewModel) 225 | -------------------------------------------------------------------------------- /pages/Functional-Programming.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Functional Programming in PHP 4 | sitemap: true 5 | --- 6 | 7 | # PHP における関数型プログラミング 8 | 9 | PHP は、ファーストクラスの関数をサポートしている。 10 | つまり、関数を変数に代入できるってことだ。 11 | 自分で定義した関数だろうがもともと組み込まれている関数だろうが、 12 | 変数で参照したり動的に実行したりできる。 13 | 何かの関数を別の関数の引数として渡すこともできるし、関数の返り値を別の関数にすることもできる(高階関数っていう機能)。 14 | 15 | 再帰 (ある関数の中から自分自身を呼ぶこと) も PHP の機能としてサポートしている。 16 | しかし、たいていの PHP コードはそれよりも逐次処理を重視している。 17 | 18 | 新型の無名関数 (クロージャにも対応したもの) が使えるようになったのは、PHP 5.3 (2009 年) 以降だ。 19 | 20 | PHP 5.4 からは、クロージャをオブジェクトのスコープにバインドできるようになった。 21 | また callable のサポートも強化され、ほとんどの場合で無名関数と互換性を持つようになった。 22 | 23 | 高階関数の最もよくある使いかたは、ストラテジーパターンの実装だ。 24 | 組み込みの `array_filter` 関数は、入力の配列 (データ) 25 | と関数 (ストラテジーあるいはコールバック) を受け取って、 26 | 配列の各要素に対してその関数をフィルターとして適用する。 27 | 28 | {% highlight php %} 29 | $min を通すもの) を作る 59 | * 60 | * 「n より大きい」フィルター群の中からひとつのフィルターを返す 61 | */ 62 | function criteria_greater_than($min) 63 | { 64 | return function($item) use ($min) { 65 | return $item > $min; 66 | }; 67 | } 68 | 69 | $input = array(1, 2, 3, 4, 5, 6); 70 | 71 | // array_filter を使い、入力値とフィルター関数を指定する 72 | $output = array_filter($input, criteria_greater_than(3)); 73 | 74 | print_r($output); // items > 3 75 | {% endhighlight %} 76 | 77 | フィルター関数群の各関数は、何らかの最小値を上回る要素だけを通過させる。 78 | `criteria_greater_than` が返すフィルターはクロージャであり、 79 | `$min` 引数の値 (`criteria_greater_than` の呼び出し時に引数として渡したもの) 80 | がスコープ内で束縛されている。 81 | 82 | デフォルトでは、`$min` 変数の値を関数にインポートするときに早期束縛を使う。 83 | 遅延束縛を使った真のクロージャを使いたい場合は、インポートの際に参照を使わないといけない。 84 | テンプレート、あるいは入力バリデーションライブラリを考えてみよう。 85 | こんな場合、クロージャを定義してスコープ内の変数を捕捉しておいて、 86 | あとで無名関数が評価されたときにそれにアクセスすることになる。 87 | 88 | * [無名関数][anonymous-functions] 89 | * [クロージャの詳細を知りたければ、RFCを読めばいいよ][closures-rfc] 90 | * [`call_user_func_array()`による動的な関数実行][call-user-func-array] 91 | 92 | 93 | [anonymous-functions]: https://www.php.net/functions.anonymous 94 | [closures-rfc]: https://wiki.php.net/rfc/closures 95 | [call-user-func-array]: https://www.php.net/function.call-user-func-array 96 | -------------------------------------------------------------------------------- /pages/example.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Example Stand-Alone Page 4 | --- 5 | 6 | # Page Title 7 | 8 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 9 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 10 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 11 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 12 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 13 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 14 | 15 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 16 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 17 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 18 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 19 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 20 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 21 | 22 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 23 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 24 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 25 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 26 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 27 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 28 | -------------------------------------------------------------------------------- /scripts/fastclick.js: -------------------------------------------------------------------------------- 1 | /** Shrinkwrap URL: 2 | * /v2/bundles/js?modules=fastclick%401.0.6%2Co-autoinit%401.0.1&shrinkwrap= 3 | */ 4 | !function(t){function e(o){if(n[o])return n[o].exports;var i=n[o]={exports:{},id:o,loaded:!1};return t[o].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";n(1),window.Origami={fastclick:n(2),"o-autoinit":n(4)}},function(t,e){t.exports={name:"__MAIN__",dependencies:{fastclick:"fastclick#*","o-autoinit":"o-autoinit#^1.0.0"}}},function(t,e,n){t.exports=n(3)},function(t,e){"use strict";var n=!1;!function(){function e(t,n){function o(t,e){return function(){return t.apply(e,arguments)}}var r;if(n=n||{},this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.touchBoundary=n.touchBoundary||10,this.layer=t,this.tapDelay=n.tapDelay||200,this.tapTimeout=n.tapTimeout||700,!e.notNeeded(t)){for(var a=["onMouse","onClick","onTouchStart","onTouchMove","onTouchEnd","onTouchCancel"],c=this,s=0,u=a.length;u>s;s++)c[a[s]]=o(c[a[s]],c);i&&(t.addEventListener("mouseover",this.onMouse,!0),t.addEventListener("mousedown",this.onMouse,!0),t.addEventListener("mouseup",this.onMouse,!0)),t.addEventListener("click",this.onClick,!0),t.addEventListener("touchstart",this.onTouchStart,!1),t.addEventListener("touchmove",this.onTouchMove,!1),t.addEventListener("touchend",this.onTouchEnd,!1),t.addEventListener("touchcancel",this.onTouchCancel,!1),Event.prototype.stopImmediatePropagation||(t.removeEventListener=function(e,n,o){var i=Node.prototype.removeEventListener;"click"===e?i.call(t,e,n.hijacked||n,o):i.call(t,e,n,o)},t.addEventListener=function(e,n,o){var i=Node.prototype.addEventListener;"click"===e?i.call(t,e,n.hijacked||(n.hijacked=function(t){t.propagationStopped||n(t)}),o):i.call(t,e,n,o)}),"function"==typeof t.onclick&&(r=t.onclick,t.addEventListener("click",function(t){r(t)},!1),t.onclick=null)}}var o=navigator.userAgent.indexOf("Windows Phone")>=0,i=navigator.userAgent.indexOf("Android")>0&&!o,r=/iP(ad|hone|od)/.test(navigator.userAgent)&&!o,a=r&&/OS 4_\d(_\d)?/.test(navigator.userAgent),c=r&&/OS [6-7]_\d/.test(navigator.userAgent),s=navigator.userAgent.indexOf("BB10")>0;e.prototype.needsClick=function(t){switch(t.nodeName.toLowerCase()){case"button":case"select":case"textarea":if(t.disabled)return!0;break;case"input":if(r&&"file"===t.type||t.disabled)return!0;break;case"label":case"iframe":case"video":return!0}return/\bneedsclick\b/.test(t.className)},e.prototype.needsFocus=function(t){switch(t.nodeName.toLowerCase()){case"textarea":return!0;case"select":return!i;case"input":switch(t.type){case"button":case"checkbox":case"file":case"image":case"radio":case"submit":return!1}return!t.disabled&&!t.readOnly;default:return/\bneedsfocus\b/.test(t.className)}},e.prototype.sendClick=function(t,e){var n,o;document.activeElement&&document.activeElement!==t&&document.activeElement.blur(),o=e.changedTouches[0],n=document.createEvent("MouseEvents"),n.initMouseEvent(this.determineEventType(t),!0,!0,window,1,o.screenX,o.screenY,o.clientX,o.clientY,!1,!1,!1,!1,0,null),n.forwardedTouchEvent=!0,t.dispatchEvent(n)},e.prototype.determineEventType=function(t){return i&&"select"===t.tagName.toLowerCase()?"mousedown":"click"},e.prototype.focus=function(t){var e;r&&t.setSelectionRange&&0!==t.type.indexOf("date")&&"time"!==t.type&&"month"!==t.type?(e=t.value.length,t.setSelectionRange(e,e)):t.focus()},e.prototype.updateScrollParent=function(t){var e,n;if(e=t.fastClickScrollParent,!e||!e.contains(t)){n=t;do{if(n.scrollHeight>n.offsetHeight){e=n,t.fastClickScrollParent=n;break}n=n.parentElement}while(n)}e&&(e.fastClickLastScrollTop=e.scrollTop)},e.prototype.getTargetElementFromEventTarget=function(t){return t.nodeType===Node.TEXT_NODE?t.parentNode:t},e.prototype.onTouchStart=function(t){var e,n,o;if(t.targetTouches.length>1)return!0;if(e=this.getTargetElementFromEventTarget(t.target),n=t.targetTouches[0],r){if(o=window.getSelection(),o.rangeCount&&!o.isCollapsed)return!0;if(!a){if(n.identifier&&n.identifier===this.lastTouchIdentifier)return t.preventDefault(),!1;this.lastTouchIdentifier=n.identifier,this.updateScrollParent(e)}}return this.trackingClick=!0,this.trackingClickStart=t.timeStamp,this.targetElement=e,this.touchStartX=n.pageX,this.touchStartY=n.pageY,t.timeStamp-this.lastClickTimen||Math.abs(e.pageY-this.touchStartY)>n?!0:!1},e.prototype.onTouchMove=function(t){return this.trackingClick?((this.targetElement!==this.getTargetElementFromEventTarget(t.target)||this.touchHasMoved(t))&&(this.trackingClick=!1,this.targetElement=null),!0):!0},e.prototype.findControl=function(t){return void 0!==t.control?t.control:t.htmlFor?document.getElementById(t.htmlFor):t.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")},e.prototype.onTouchEnd=function(t){var e,n,o,s,u,l=this.targetElement;if(!this.trackingClick)return!0;if(t.timeStamp-this.lastClickTimethis.tapTimeout)return!0;if(this.cancelNextClick=!1,this.lastClickTime=t.timeStamp,n=this.trackingClickStart,this.trackingClick=!1,this.trackingClickStart=0,c&&(u=t.changedTouches[0],l=document.elementFromPoint(u.pageX-window.pageXOffset,u.pageY-window.pageYOffset)||l,l.fastClickScrollParent=this.targetElement.fastClickScrollParent),o=l.tagName.toLowerCase(),"label"===o){if(e=this.findControl(l)){if(this.focus(l),i)return!1;l=e}}else if(this.needsFocus(l))return t.timeStamp-n>100||r&&window.top!==window&&"input"===o?(this.targetElement=null,!1):(this.focus(l),this.sendClick(l,t),r&&"select"===o||(this.targetElement=null,t.preventDefault()),!1);return r&&!a&&(s=l.fastClickScrollParent,s&&s.fastClickLastScrollTop!==s.scrollTop)?!0:(this.needsClick(l)||(t.preventDefault(),this.sendClick(l,t)),!1)},e.prototype.onTouchCancel=function(){this.trackingClick=!1,this.targetElement=null},e.prototype.onMouse=function(t){return this.targetElement?t.forwardedTouchEvent?!0:t.cancelable&&(!this.needsClick(this.targetElement)||this.cancelNextClick)?(t.stopImmediatePropagation?t.stopImmediatePropagation():t.propagationStopped=!0,t.stopPropagation(),t.preventDefault(),!1):!0:!0},e.prototype.onClick=function(t){var e;return this.trackingClick?(this.targetElement=null,this.trackingClick=!1,!0):"submit"===t.target.type&&0===t.detail?!0:(e=this.onMouse(t),e||(this.targetElement=null),e)},e.prototype.destroy=function(){var t=this.layer;i&&(t.removeEventListener("mouseover",this.onMouse,!0),t.removeEventListener("mousedown",this.onMouse,!0),t.removeEventListener("mouseup",this.onMouse,!0)),t.removeEventListener("click",this.onClick,!0),t.removeEventListener("touchstart",this.onTouchStart,!1),t.removeEventListener("touchmove",this.onTouchMove,!1),t.removeEventListener("touchend",this.onTouchEnd,!1),t.removeEventListener("touchcancel",this.onTouchCancel,!1)},e.notNeeded=function(t){var e,n,o,r;if("undefined"==typeof window.ontouchstart)return!0;if(n=+(/Chrome\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]){if(!i)return!0;if(e=document.querySelector("meta[name=viewport]")){if(-1!==e.content.indexOf("user-scalable=no"))return!0;if(n>31&&document.documentElement.scrollWidth<=window.outerWidth)return!0}}if(s&&(o=navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/),o[1]>=10&&o[2]>=3&&(e=document.querySelector("meta[name=viewport]")))){if(-1!==e.content.indexOf("user-scalable=no"))return!0;if(document.documentElement.scrollWidth<=window.outerWidth)return!0}return"none"===t.style.msTouchAction||"manipulation"===t.style.touchAction?!0:(r=+(/Firefox\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1],r>=27&&(e=document.querySelector("meta[name=viewport]"),e&&(-1!==e.content.indexOf("user-scalable=no")||document.documentElement.scrollWidth<=window.outerWidth))?!0:"none"===t.style.touchAction||"manipulation"===t.style.touchAction?!0:!1)},e.attach=function(t,n){return new e(t,n)},"function"==typeof n&&"object"==typeof n.amd&&n.amd?n(function(){return e}):"undefined"!=typeof t&&t.exports?(t.exports=e.attach,t.exports.FastClick=e):window.FastClick=e}()},function(t,e,n){t.exports=n(5)},function(t,e){"use strict";function n(t){t in o||(o[t]=!0,document.dispatchEvent(new CustomEvent("o."+t)))}var o={};window.addEventListener("load",n.bind(null,"load")),window.addEventListener("load",n.bind(null,"DOMContentLoaded")),document.addEventListener("DOMContentLoaded",n.bind(null,"DOMContentLoaded")),"complete"===document.readyState?(n("load"),n("DOMContentLoaded")):"interactive"===document.readyState&&n("DOMContentLoaded")}]); 5 | -------------------------------------------------------------------------------- /scripts/setup.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | // Attach FastClick 3 | var attachFastClick = Origami.fastclick; 4 | attachFastClick(document.body); 5 | 6 | // Mobile TOC menu 7 | var $window = $(window), 8 | $nav = $('.site-navigation'); 9 | $nav.click(function (e) { 10 | var $target = $(e.target); 11 | if ($target.is($nav) && $window.width() <= 375) { 12 | $nav.toggleClass('open'); 13 | } 14 | if ($target.is('a')) { 15 | $nav.removeClass('open'); 16 | } 17 | }); 18 | })(jQuery); 19 | -------------------------------------------------------------------------------- /styles/all.less: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | NMC Bootstrap 3 | 4 | This LESS file imports all other LESS files. You should compile 5 | and minify this file before site launch. 6 | ========================================================================== */ 7 | 8 | /* Import NMC bootstrap */ 9 | 10 | @import "base/all"; 11 | 12 | /* Import site-specific styles */ 13 | 14 | @import "site/site-header.less"; 15 | @import "site/site-navigation.less"; 16 | @import "site/site-content.less"; 17 | @import "site/site-footer.less"; 18 | 19 | /* Tablets and Smartphones */ 20 | 21 | @media only screen and (max-width : 1024px) { 22 | .build-date{ 23 | text-align: center; 24 | } 25 | .site-header{ 26 | height: 220px; 27 | position: absolute; 28 | top: 20px; 29 | left: 0; 30 | width: 100%; 31 | } 32 | .fork-me img{ 33 | height: 110px; 34 | width: 110px; 35 | } 36 | .site-navigation{ 37 | margin-top: 240px; 38 | padding: 20px; 39 | position: relative; 40 | width: auto; 41 | 42 | ul{ 43 | border: 1px solid #999; 44 | border-bottom: none; 45 | } 46 | li{ 47 | .man; 48 | .pan; 49 | } 50 | a{ 51 | background: #CCC; 52 | display: block; 53 | border-bottom: 1px solid #999; 54 | padding: 10px; 55 | text-decoration: none; 56 | } 57 | ul ul{ 58 | border: none; 59 | .man; 60 | .pan; 61 | } 62 | ul ul a{ 63 | background: transparent; 64 | } 65 | } 66 | .site-content{ 67 | padding: 20px; 68 | } 69 | .top{ 70 | display: inline-block; 71 | float: none; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /styles/base/all.less: -------------------------------------------------------------------------------- 1 | @import "reset"; 2 | @import "prefixer"; 3 | @import "spacing"; 4 | @import "typography"; 5 | @import "idioms"; 6 | @import "grid"; 7 | @import "bars-buttons"; 8 | @import "buttons"; -------------------------------------------------------------------------------- /styles/base/bars-buttons.less: -------------------------------------------------------------------------------- 1 | .button() { 2 | .border-box; 3 | cursor: pointer; 4 | display: inline-block; 5 | .phh; 6 | text-align: center; 7 | font-weight: bold; 8 | } 9 | .button-hover(){ 10 | text-decoration: none; 11 | } 12 | .bar() { 13 | display: block; 14 | .pah; 15 | text-align: center; 16 | font-weight: bold; 17 | } 18 | 19 | /* Sizes */ 20 | 21 | .btn-size(@scale){ 22 | height: @baseline * @scale !important; 23 | padding-bottom: 0 !important; 24 | padding-top: 0 !important; 25 | line-height: @baseline * (@scale * 0.9); 26 | } 27 | .btn-half{ 28 | .btn-size(1); 29 | font-size: 0.8em; 30 | } 31 | .btn-single{ 32 | .btn-size(1.5); 33 | font-size: 1em; 34 | } 35 | .btn-double{ 36 | .btn-size(2); 37 | font-size: 1.1em; 38 | } 39 | 40 | /* Shapes */ 41 | 42 | .bb-shape-square() { 43 | .border-radius(0); 44 | } 45 | .bb-shape-rounded(@rad:3px) { 46 | .border-radius(@rad); 47 | } 48 | .bb-shape-round() { 49 | .border-radius(@baseline); 50 | } 51 | 52 | /* Text */ 53 | 54 | .bb-text-dark(){ 55 | color: #333; 56 | text-shadow: 0 1px 0 #fff; 57 | } 58 | .bb-text-light(){ 59 | color: #fff; 60 | text-shadow: 0 -1px 0 rgba(0,0,0,.3); 61 | } 62 | .bb-text-color(@color){ 63 | 64 | } 65 | 66 | /* Color */ 67 | 68 | .bb-color-plain(@color){ 69 | background: @color; 70 | } 71 | .bb-color-gradient(@color){ 72 | .gradient(@color,lighten(@color,10%),darken(@color,10%)); 73 | } 74 | .bb-color-soft(@color){ 75 | .gradient(@color,lighten(@color,5%),darken(@color,5%)); 76 | } 77 | .bb-color-gloss(@color){ 78 | @topStart: desaturate(lighten(@color,40%),20%); 79 | @topStop: desaturate(lighten(@color,20%),40%); 80 | @bottomStart: desaturate(lighten(@color,10%),30%); 81 | @bottomStop: desaturate(lighten(@color,15%),30%); 82 | .linear-gradient-top(@color,@topStart,0%,@topStop,50%,@bottomStart,50%,@bottomStop,100%); 83 | } 84 | 85 | /* Border */ 86 | 87 | .bb-border-noborder(){ 88 | border: none; 89 | } 90 | .bb-border-plain(@color){ 91 | border: 1px solid darken(@color,10%); 92 | } 93 | .bb-border-contrast(@color){ 94 | border: 1px solid darken(@color,15%); 95 | .box-shadow(inset 0 0 1px 1px lighten(@color,15%)); 96 | } 97 | .bb-border-meta(@color){ 98 | border: 1px solid darken(@color,15%); 99 | .box-shadow(inset 0 2px 1px -1px lighten(@color,20%)); 100 | } 101 | 102 | /* Minimal */ 103 | 104 | .button-minimal(@color) { 105 | .button(); 106 | .bb-shape-rounded(); 107 | .bb-color-plain(@color); 108 | .bb-border-contrast(@color); 109 | .bb-text-dark(); 110 | } 111 | .button-minimal-hover(@color){ 112 | .button-minimal(darken(@color,5%)); 113 | .button-hover(); 114 | } 115 | .button-minimal-active(@color){ 116 | .button-minimal-hover(darken(@color,5%)); 117 | } 118 | .bar-minimal(@color) { 119 | .button-minimal(@color); 120 | .bar(); 121 | } 122 | 123 | /* Clean */ 124 | 125 | .button-clean(@color) { 126 | .button(); 127 | .bb-shape-rounded(); 128 | .bb-color-gradient(@color); 129 | .bb-border-plain(darken(@color,5%)); 130 | .bb-text-dark(); 131 | } 132 | .button-clean-hover(@color){ 133 | .button-clean(darken(@color,5%)); 134 | .button-hover(); 135 | } 136 | .button-clean-active(@color){ 137 | .button-clean-hover(darken(@color,5%)); 138 | } 139 | .bar-clean(@color){ 140 | .button-clean(@color); 141 | .bar(); 142 | } 143 | 144 | /* Soft */ 145 | 146 | .button-soft(@color) { 147 | .button(); 148 | .bb-shape-rounded(); 149 | .bb-color-soft(@color); 150 | .bb-border-meta(darken(@color,5%)); 151 | .bb-text-light(); 152 | } 153 | .button-soft-hover(@color){ 154 | .button-soft(darken(@color,5%)); 155 | .button-hover(); 156 | } 157 | .button-soft-active(@color){ 158 | .button-soft-hover(darken(@color,5%)); 159 | } 160 | .bar-soft(@color){ 161 | .button-soft(@color); 162 | .bar(); 163 | } 164 | 165 | /* Pill */ 166 | 167 | .button-pill(@color) { 168 | .button(); 169 | .bb-shape-round(); 170 | .bb-color-soft(@color); 171 | .bb-border-meta(darken(@color,5%)); 172 | .bb-text-light(); 173 | } 174 | .button-pill-hover(@color){ 175 | .button-pill(darken(@color,5%)); 176 | .button-hover(); 177 | } 178 | .button-pill-active(@color){ 179 | .button-pill-hover(darken(@color,5%)); 180 | } 181 | .bar-pill(@color){ 182 | .button-pill(@color); 183 | .bar(); 184 | } 185 | 186 | /* Gloss */ 187 | 188 | .button-gloss(@color) { 189 | .button(); 190 | .bb-shape-rounded(5px); 191 | .bb-color-gloss(@color); 192 | .bb-border-plain(darken(@color,5%)); 193 | .box-shadow(inset 0 1px 0 0 rgba(255,255,255,.5)); 194 | .bb-text-light(); 195 | } 196 | .button-gloss-hover(@color){ 197 | .button-gloss(darken(@color,5%)); 198 | .box-shadow(inset 0 1px 0 0 rgba(255,255,255,.3)); 199 | .button-hover(); 200 | } 201 | .button-gloss-active(@color){ 202 | .button-gloss-hover(darken(@color,5%)); 203 | .box-shadow(inset 0 0 5px 0 rgba(0,0,0,.3)); 204 | } 205 | .bar-gloss(@color){ 206 | .button-gloss(@color); 207 | .bar(); 208 | } 209 | 210 | @btn-minimal-color: #eee; 211 | .btn-minimal { .button-minimal(@btn-minimal-color); } 212 | .btn-minimal:hover { .button-minimal-hover(@btn-minimal-color); } 213 | .btn-minimal:active { .button-minimal-active(@btn-minimal-color); } 214 | 215 | @btn-clean-color: #eee; 216 | .btn-clean { .button-clean(@btn-clean-color); } 217 | .btn-clean:hover { .button-clean-hover(@btn-clean-color); } 218 | .btn-clean:active { .button-clean-active(@btn-clean-color); } 219 | 220 | @btn-soft-color: #6C84AB; 221 | .btn-soft { .button-soft(@btn-soft-color); } 222 | .btn-soft:hover { .button-soft-hover(@btn-soft-color); } 223 | .btn-soft:active { .button-soft-active(@btn-soft-color); } 224 | 225 | @btn-pill-color: #6C84AB; 226 | .btn-pill { .button-pill(@btn-pill-color); } 227 | .btn-pill:hover { .button-pill-hover(@btn-pill-color); } 228 | .btn-pill:active { .button-pill-active(@btn-pill-color); } 229 | 230 | @btn-gloss-color: #172D6E; 231 | .btn-gloss { .button-gloss(@btn-gloss-color); } 232 | .btn-gloss:hover { .button-gloss-hover(@btn-gloss-color); } 233 | .btn-gloss:active { .button-gloss-active(@btn-gloss-color); } 234 | 235 | 236 | .bar-minimal { .bar-minimal(@btn-minimal-color); } 237 | .bar-clean { .bar-clean(@btn-clean-color); } 238 | .bar-soft { .bar-soft(@btn-soft-color); } 239 | .bar-pill { .bar-pill(@btn-pill-color); } 240 | .bar-gloss { .bar-gloss(@btn-gloss-color); } -------------------------------------------------------------------------------- /styles/base/buttons.less: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | Settings 3 | ========================================================================== */ 4 | 5 | @import 'variables.less'; 6 | 7 | /* 8 | @baseline: @baseline; 9 | @button-color: @button-color; 10 | @button-primary-color: @button-primary-color; 11 | @button-info-color: @button-info-color; 12 | @button-success-color: @button-success-color; 13 | @button-warning-color: @button-warning-color; 14 | @button-danger-color: @button-danger-color; 15 | */ 16 | 17 | /* ========================================================================== 18 | Default 19 | ========================================================================== */ 20 | 21 | .btn{ 22 | .btn-s; 23 | background-clip: border-box !important; 24 | background-repeat: repeat-x; 25 | border: 1px solid rgba(0, 0, 0, 0.25); 26 | .border-box; 27 | .border-radius(4px); 28 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); 29 | cursor: pointer; 30 | display: inline-block; 31 | .gradient(@button-color, lighten(@button-color, 10%), @button-color); 32 | .phh; 33 | .pvn; 34 | .transition(background-position linear 0.1s); 35 | vertical-align: middle; 36 | color: #333; 37 | font-family: @body-font-family; 38 | text-decoration: none !important; 39 | text-shadow: rgba(255,255,255,0.75) 0 1px 0; 40 | 41 | &:hover{ 42 | background-position: 0 -15px; 43 | } 44 | } 45 | 46 | /* ========================================================================== 47 | Styles 48 | ========================================================================== */ 49 | 50 | .btn-primary, 51 | .btn-info, 52 | .btn-success, 53 | .btn-warning, 54 | .btn-danger{ 55 | color: #FFF !important; 56 | text-shadow: rgba(0,0,0,0.25) 0 -1px 0 !important; 57 | } 58 | .btn-primary{ 59 | .btn; 60 | .gradient(@button-primary-color, lighten(@button-primary-color, 10%), @button-primary-color); 61 | } 62 | .btn-info{ 63 | .btn; 64 | .gradient(@button-info-color, lighten(@button-info-color, 10%), @button-info-color); 65 | } 66 | .btn-success{ 67 | .btn; 68 | .gradient(@button-success-color, lighten(@button-success-color, 10%), @button-success-color); 69 | } 70 | .btn-warning{ 71 | .btn; 72 | .gradient(@button-warning-color, lighten(@button-warning-color, 10%), @button-warning-color); 73 | } 74 | .btn-danger{ 75 | .btn; 76 | .gradient(@button-danger-color, lighten(@button-danger-color, 10%), @button-danger-color); 77 | } 78 | 79 | /* ========================================================================== 80 | Sizes (Half = h, Single = s, Double = d) 81 | ========================================================================== */ 82 | 83 | .btn-h, .btn-half{ 84 | height: @baseline; 85 | font-size: @baseline * 0.6; 86 | line-height: @baseline; 87 | } 88 | .btn-s, .btn-single{ 89 | height: @baseline * 1.5; 90 | font-size: @baseline * 0.75; 91 | line-height: @baseline * 1.5; 92 | } 93 | .btn-d, .btn-double{ 94 | height: @baseline * 2; 95 | font-size: @baseline; 96 | line-height: @baseline * 2; 97 | } -------------------------------------------------------------------------------- /styles/base/grid.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Hybrid Grid Sytem 3 | * 4 | * Blend of the Semantic Grid System and Zurb Foundation with a little Twitter Bootstrap 5 | */ 6 | 7 | /* Settings */ 8 | 9 | @import 'variables.less'; 10 | 11 | /* 12 | @fixed-column-width: 40px; 13 | @fixed-gutter-width: 20px; 14 | @fixed-columns: 12; 15 | 16 | @fluid-column-width: 4.3%; 17 | @fluid-gutter-width: 4.4%; 18 | @fluid-columns: 12; 19 | 20 | @mobile-break-width: 480px; 21 | @mobile-column-width: 8.6%; 22 | @mobile-gutter-width: 8.8%; 23 | @mobile-columns: 6; 24 | */ 25 | 26 | /* Grid */ 27 | 28 | #grid { 29 | 30 | .cols(@cols,@width,@gutter){ 31 | .border-box(); 32 | width: ((@cols * @width) + ((@cols - 1) * @gutter)); 33 | margin-left: @gutter; 34 | position: relative; 35 | display: inline; 36 | float: left; 37 | min-height: 1px; 38 | &:first-child { 39 | margin-left: 0; 40 | } 41 | &:last-child { 42 | float: right; 43 | } 44 | } 45 | 46 | } 47 | 48 | .grid-fixed,.grid-fluid { 49 | .clearfix; 50 | .row { 51 | .border-box(); 52 | display: block; 53 | width: 100%; 54 | margin: 0 auto; 55 | .clearfix; 56 | 57 | .center,.center:last-child { 58 | float: none; 59 | display: block; 60 | margin: 0 auto; 61 | } 62 | } 63 | } 64 | 65 | .grid-fixed { 66 | @total-width: (@fixed-column-width*@fixed-columns) + (@fixed-gutter-width*(@fixed-columns - 1)); 67 | @column-width: @fixed-column-width; 68 | @gutter-width: @fixed-gutter-width; 69 | @columns: @fixed-columns; 70 | width: @total-width; 71 | 72 | /* This is duplicated in both classes. Unavoidable. */ 73 | .colX (@index) when (@index > 0) { 74 | (~".col@{index}") { 75 | #grid > .cols(@index,@column-width,@gutter-width); 76 | } 77 | .colX(@index - 1); 78 | } 79 | .colX (0) {} 80 | .colX(@columns); 81 | 82 | .offsetX (@index) when (@index > 0) { 83 | (~".offset@{index}") { 84 | margin-left: (@index * @column-width) + ((@index + 1) * @gutter-width); 85 | } 86 | .offsetX(@index - 1); 87 | } 88 | .offsetX (0) {} 89 | .offsetX(@columns - 1); 90 | 91 | .pushX (@index) when (@index > 0) { 92 | (~".push@{index}") { 93 | left: @index * (@column-width + @gutter-width); 94 | } 95 | .pushX(@index - 1); 96 | } 97 | .pushX (0) {} 98 | .pushX(@columns - 1); 99 | 100 | .pullX (@index) when (@index > 0) { 101 | (~".pull@{index}") { 102 | right: @index * (@column-width + @gutter-width); 103 | } 104 | .pullX(@index - 1); 105 | } 106 | .pullX (0) {} 107 | .pullX(@columns - 1); 108 | } 109 | 110 | 111 | .grid-fluid { 112 | @total-width: 100%; 113 | @column-width: @fluid-column-width; 114 | @gutter-width: @fluid-gutter-width; 115 | @columns: @fluid-columns; 116 | width: @total-width; 117 | 118 | /* This is duplicated in both classes. Unavoidable. */ 119 | .colX (@index) when (@index > 0) { 120 | (~".col@{index}") { 121 | #grid > .cols(@index,@column-width,@gutter-width); 122 | } 123 | .colX(@index - 1); 124 | } 125 | .colX (0) {} 126 | .colX(@columns); 127 | 128 | .offsetX (@index) when (@index > 0) { 129 | (~".offset@{index}") { 130 | margin-left: (@index * @column-width) + ((@index + 1) * @gutter-width); 131 | } 132 | .offsetX(@index - 1); 133 | } 134 | .offsetX (0) {} 135 | .offsetX(@columns - 1); 136 | 137 | .pushX (@index) when (@index > 0) { 138 | (~".push@{index}") { 139 | left: @index * (@column-width + @gutter-width); 140 | } 141 | .pushX(@index - 1); 142 | } 143 | .pushX (0) {} 144 | .pushX(@columns - 1); 145 | 146 | .pullX (@index) when (@index > 0) { 147 | (~".pull@{index}") { 148 | right: @index * (@column-width + @gutter-width); 149 | } 150 | .pullX(@index - 1); 151 | } 152 | .pullX (0) {} 153 | .pullX(@columns - 1); 154 | } 155 | 156 | 157 | @media all and (max-width: @mobile-break-width) { 158 | 159 | // Reset all columns to full width 160 | .grid-fixed { 161 | .colX (@index) when (@index > 0) { 162 | (~".col@{index}") { 163 | width: 100%; 164 | margin: 0; 165 | left: 0; 166 | right: 0; 167 | } 168 | .colX(@index - 1); 169 | } 170 | .colX (0) {} 171 | .colX(@fixed-columns); 172 | } 173 | .grid-fluid { 174 | .colX (@index) when (@index > 0) { 175 | (~".col@{index}") { 176 | width: 100%; 177 | margin: 0; 178 | left: 0; 179 | right: 0; 180 | } 181 | .colX(@index - 1); 182 | } 183 | .colX (0) {} 184 | .colX(@fluid-columns); 185 | } 186 | 187 | .grid-fixed, .grid-fluid { 188 | @total-width: 100%; 189 | @column-width: @mobile-column-width; 190 | @gutter-width: @mobile-gutter-width; 191 | @columns: @mobile-columns; 192 | width: @total-width; 193 | 194 | .m-colX (@index) when (@index > 0) { 195 | (~".m-col@{index}") { 196 | #grid > .cols(@index,@column-width,@gutter-width); 197 | } 198 | .m-colX(@index - 1); 199 | } 200 | .m-colX (0) {} 201 | .m-colX(@mobile-columns); 202 | 203 | .m-offsetX (@index) when (@index > 0) { 204 | (~".m-offset@{index}") { 205 | margin-left: (@index * @column-width) + ((@index + 1) * @gutter-width); 206 | } 207 | .m-offsetX(@index - 1); 208 | } 209 | .m-offsetX (0) {} 210 | .m-offsetX(@columns - 1); 211 | 212 | .m-pushX (@index) when (@index > 0) { 213 | (~".m-push@{index}") { 214 | left: @index * (@column-width + @gutter-width); 215 | } 216 | .m-pushX(@index - 1); 217 | } 218 | .m-pushX (0) {} 219 | .m-pushX(@columns - 1); 220 | 221 | .m-pullX (@index) when (@index > 0) { 222 | (~".m-pull@{index}") { 223 | right: @index * (@column-width + @gutter-width); 224 | } 225 | .m-pullX(@index - 1); 226 | } 227 | .m-pullX (0) {} 228 | .m-pullX(@columns - 1); 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /styles/base/idioms.less: -------------------------------------------------------------------------------- 1 | /** 2 | * New Media Campaigns Idioms 3 | * 4 | * These are common patterns we use in all of our 5 | * projects. They are consolidated here to keep code DRY. 6 | * 7 | * Listing 8 | * * .no-text, .text-replace 9 | * * .no-list 10 | * * .no-form 11 | * * .fixed-width(@width) 12 | * * .column-width(@width) 13 | * * .column-left(@width) 14 | * * .column-right(@width) 15 | * * .full-size 16 | * * .absolute-default 17 | * * .absolute-fullsize 18 | * * .clearfix 19 | */ 20 | 21 | /* Hides text when using image replacement */ 22 | .no-text, .text-replace{ 23 | overflow: hidden; 24 | text-indent: 100%; 25 | white-space: nowrap; 26 | } 27 | 28 | /* Removes bullets, margin, and padding from list */ 29 | .no-list{ 30 | list-style: none; 31 | margin: 0; 32 | padding: 0; 33 | } 34 | 35 | /* Removes webkit styling from form element */ 36 | .no-form{ 37 | border: none; 38 | margin: 0; 39 | padding: 0; 40 | -webkit-appearance: none; 41 | } 42 | 43 | /* Center a fixed width container */ 44 | .fixed-width(@width) { 45 | margin: 0 auto; 46 | width: @width; 47 | } 48 | 49 | /* Adds left or right columns (e.g. content and sidebar) */ 50 | .column-width(@width){ 51 | display: inline; 52 | width: @width; 53 | } 54 | .column-left(@width){ 55 | .column-width(@width); 56 | float: left; 57 | } 58 | .column-right(@width){ 59 | .column-width(@width); 60 | float: right; 61 | } 62 | 63 | /* Set width and height of element to that of its parent */ 64 | .full-size{ 65 | height: 100%; 66 | width: 100%; 67 | } 68 | 69 | /* Position element absolutely to 0,0 */ 70 | .absolute-default{ 71 | position: absolute; 72 | left: 0; 73 | top: 0; 74 | } 75 | 76 | /* Position element absolutely and set its width and height to that of its parent (useful for slideshows) */ 77 | .absolute-fullsize{ 78 | .absolute-default; 79 | .full-size; 80 | } 81 | 82 | /* The micro clearfix http://nicolasgallagher.com/micro-clearfix-hack/ */ 83 | .clearfix { 84 | *zoom:1; 85 | 86 | &:before, 87 | &:after { 88 | content:""; 89 | display:table; 90 | } 91 | &:after { 92 | clear:both; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /styles/base/reset.less: -------------------------------------------------------------------------------- 1 | /** 2 | * html5doctor.com Reset Stylesheet 3 | * v1.6.1 4 | * Last Updated: 2010-09-17 5 | * Author: Richard Clark - http://richclarkdesign.com 6 | * Twitter: @rich_clark 7 | */ 8 | 9 | html, body, div, span, object, iframe, 10 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 11 | abbr, address, cite, code, 12 | del, dfn, em, img, ins, kbd, q, samp, 13 | small, strong, sub, sup, var, 14 | b, i, 15 | dl, dt, dd, ol, ul, li, 16 | fieldset, form, label, legend, 17 | table, caption, tbody, tfoot, thead, tr, th, td, 18 | article, aside, canvas, details, figcaption, figure, 19 | footer, header, hgroup, menu, nav, section, summary, 20 | time, mark, audio, video { 21 | margin:0; 22 | padding:0; 23 | border:0; 24 | outline:0; 25 | font-size:100%; 26 | vertical-align:baseline; 27 | background:transparent; 28 | } 29 | 30 | body { 31 | line-height:1; 32 | } 33 | 34 | article,aside,details,figcaption,figure, 35 | footer,header,hgroup,menu,nav,section { 36 | display:block; 37 | } 38 | 39 | nav ul { 40 | list-style:none; 41 | } 42 | 43 | blockquote, q { 44 | quotes:none; 45 | } 46 | 47 | blockquote:before, blockquote:after, 48 | q:before, q:after { 49 | content:''; 50 | content:none; 51 | } 52 | 53 | a { 54 | margin:0; 55 | padding:0; 56 | font-size:100%; 57 | vertical-align:baseline; 58 | background:transparent; 59 | } 60 | 61 | /* change colours to suit your needs */ 62 | ins { 63 | background-color:#ff9; 64 | color:#000; 65 | text-decoration:none; 66 | } 67 | 68 | /* change colours to suit your needs */ 69 | mark { 70 | background-color:#ff9; 71 | color:#000; 72 | font-style:italic; 73 | font-weight:bold; 74 | } 75 | 76 | del { 77 | text-decoration: line-through; 78 | } 79 | 80 | abbr[title], dfn[title] { 81 | border-bottom:1px dotted; 82 | cursor:help; 83 | } 84 | 85 | table { 86 | border-collapse:collapse; 87 | border-spacing:0; 88 | } 89 | 90 | /* change border colour to suit your needs */ 91 | hr { 92 | display:block; 93 | height:1px; 94 | border:0; 95 | border-top:1px solid #cccccc; 96 | margin:1em 0; 97 | padding:0; 98 | } 99 | 100 | input, select { 101 | vertical-align:middle; 102 | } -------------------------------------------------------------------------------- /styles/base/spacing.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Spacing 3 | * 4 | * This LESS file defines margins and paddings for block-level 5 | * elements. Helper classes are included for use elsewhere 6 | * in site styles. 7 | */ 8 | 9 | /* Settings */ 10 | 11 | @import 'variables.less'; 12 | 13 | /* 14 | @baseline: @baseline; 15 | */ 16 | 17 | /** 18 | * Spacing 19 | * p, m, lh = padding, margin, line-height 20 | * a, t, r, b, l, h, v = all, top, right, bottom, left, horizontal, vertical 21 | * n, h, s, d = none(0px), half(@baseline / 2), single(@baseline), double(@baseline * 2), none(0px) 22 | */ 23 | 24 | .ptn, .pvn, .pan{ 25 | padding-top: 0 !important 26 | } 27 | .pth, .pvh, .pah{ 28 | padding-top: @baseline / 2 !important 29 | } 30 | .pts, .pvs, .pas{ 31 | padding-top: @baseline !important 32 | } 33 | .ptd, .pvd, .pad{ 34 | padding-top: @baseline * 2 !important 35 | } 36 | .prn, .phn, .pan{ 37 | padding-right: 0 !important 38 | } 39 | .prh, .phh, .pah{ 40 | padding-right: @baseline / 2 !important 41 | } 42 | .prs, .phs, .pas{ 43 | padding-right: @baseline !important 44 | } 45 | .prd, .phd, .pad{ 46 | padding-right: @baseline * 2 !important 47 | } 48 | .pbn, .pvn, .pan{ 49 | padding-bottom: 0 !important 50 | } 51 | .pbh, .pvh, .pah{ 52 | padding-bottom: @baseline / 2 !important 53 | } 54 | .pbs, .pvs, .pas{ 55 | padding-bottom: @baseline !important 56 | } 57 | .pbd, .pvd, .pad{ 58 | padding-bottom: @baseline * 2 !important 59 | } 60 | .pln, .phn, .pan{ 61 | padding-left: 0 !important 62 | } 63 | .plh, .phh, .pah{ 64 | padding-left: @baseline / 2 !important 65 | } 66 | .pls, .phs, .pas{ 67 | padding-left: @baseline !important 68 | } 69 | .pld, .phd, .pad{ 70 | padding-left: @baseline * 2 !important 71 | } 72 | .mtn, .mvn, .man{ 73 | margin-top: 0 !important 74 | } 75 | .mth, .mvh, .mah{ 76 | margin-top: @baseline / 2 !important 77 | } 78 | .mts, .mvs, .mas{ 79 | margin-top: @baseline !important 80 | } 81 | .mtd, .mvd, .mad{ 82 | margin-top: @baseline * 2 !important 83 | } 84 | .mrn, .mhn, .man{ 85 | margin-right: 0 !important 86 | } 87 | .mrh, .mhh, .mah{ 88 | margin-right: @baseline / 2 !important 89 | } 90 | .mrs, .mhs, .mas{ 91 | margin-right: @baseline !important 92 | } 93 | .mrd, .mhd, .mad{ 94 | margin-right: @baseline * 2 !important 95 | } 96 | .mbn, .mvn, .man{ 97 | margin-bottom: 0 !important 98 | } 99 | .mbh, .mvh, .mah{ 100 | margin-bottom: @baseline / 2 !important 101 | } 102 | .mbs, .mvs, .mas{ 103 | margin-bottom: @baseline !important 104 | } 105 | .mbd, .mvd, .mad{ 106 | margin-bottom: @baseline * 2 !important 107 | } 108 | .mln, .mhn, .man{ 109 | margin-left: 0 !important 110 | } 111 | .mlh, .mhh, .mah{ 112 | margin-left: @baseline / 2 !important 113 | } 114 | .mls, .mhs, .mas{ 115 | margin-left: @baseline !important 116 | } 117 | .mld, .mhd, .mad{ 118 | margin-left: @baseline * 2 !important 119 | } 120 | .lhh { 121 | line-height: @baseline / 2 !important; 122 | } 123 | .lhs { 124 | line-height: @baseline !important; 125 | } 126 | .lhd { 127 | line-height: @baseline * 2 !important; 128 | } 129 | .lhn { 130 | line-height: 0 !important; 131 | } 132 | -------------------------------------------------------------------------------- /styles/base/typography.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Name Here 3 | * 4 | * @version 1.0 5 | * @package Name Here 6 | * @author New Media Campaigns 7 | * @copyright 2012 New Media Campaigns 8 | * @link http://www.newmediacampaigns.com 9 | * 10 | * Copyright (c) 2012 New Media Campaigns 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is furnished 17 | * to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in all 20 | * copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | * THE SOFTWARE. 29 | */ 30 | 31 | /** 32 | * Typography 33 | * 34 | * This LESS file defines the baseline, color, font-size, and other typographical 35 | * styles for text elements. 36 | */ 37 | 38 | /* Settings */ 39 | 40 | @import 'variables.less'; 41 | 42 | /* 43 | @baseline: @baseline; 44 | 45 | @body-color: @body-color; 46 | @body-font-family: @body-font-family; 47 | @body-font-size: @body-font-size; 48 | @body-accent-color: @body-accent-color; 49 | 50 | @header-color: @header-clor; 51 | @header-font-family: @header-font-family; 52 | @header-font-weight: @header-font-weight; 53 | */ 54 | 55 | /* Base */ 56 | 57 | html, body { 58 | font-family: @body-font-family; 59 | color: @body-color; 60 | font-size: @body-font-size; 61 | .lhs; 62 | } 63 | 64 | /* Block-level */ 65 | 66 | h1, h2, h3, h4, h5, h6, ul, ol, dl, p, blockquote, table, form, pre{ 67 | .mbs; 68 | } 69 | 70 | /* Headers */ 71 | 72 | .all-headers{ 73 | color: @header-color; 74 | font-family: @header-font-family; 75 | font-weight: @header-font-weight; 76 | } 77 | h1, .alpha{ 78 | .all-headers; 79 | font-size: @body-font-size * 2.5; 80 | .lhd; 81 | } 82 | h2, .beta{ 83 | .all-headers; 84 | font-size: @body-font-size * 1.75; 85 | .lhd; 86 | } 87 | h3, .gamma{ 88 | .all-headers; 89 | font-size: @body-font-size * 1.2; 90 | .lhd; 91 | .mbn; 92 | } 93 | h4, .delta{ 94 | .all-headers; 95 | font-size: @body-font-size * 1; 96 | .lhd; 97 | .mbn; 98 | } 99 | h5, .epsilon{ 100 | .all-headers; 101 | font-size: @body-font-size * 1; 102 | .lhs; 103 | .mbn 104 | } 105 | h6, .zeta{ 106 | .all-headers; 107 | font-size: @body-font-size * 1; 108 | .lhs; 109 | .mbn; 110 | } 111 | 112 | /* Headers (above scale) */ 113 | 114 | .giga{ 115 | .all-headers; 116 | font-size: @body-font-size * 6; 117 | line-height: @baseline * 4; 118 | } 119 | .mega{ 120 | .all-headers; 121 | font-size: @body-font-size * 5; 122 | line-height: @baseline * 3; 123 | } 124 | .kilo{ 125 | .all-headers; 126 | font-size: @body-font-size * 4; 127 | line-height: @baseline * 3; 128 | } 129 | 130 | /* Headers (below scale) */ 131 | 132 | .milli{ 133 | .all-headers; 134 | font-size: @body-font-size * 0.8; 135 | } 136 | 137 | /* Text */ 138 | 139 | a{ 140 | color: @body-accent-color; 141 | } 142 | a:link{ 143 | text-decoration: underline; 144 | } 145 | a:visited{ 146 | 147 | } 148 | a:hover{ 149 | text-decoration: none; 150 | } 151 | a:active{ 152 | 153 | } 154 | a:focus{ 155 | 156 | } 157 | small { 158 | font-size: 80%; 159 | } 160 | sup, sub { 161 | font-size: 80%; 162 | .lhn; 163 | } 164 | sup { 165 | vertical-align: super; 166 | } 167 | sub { 168 | vertical-align: sub; 169 | } 170 | 171 | .lead{ 172 | color: darken(@body-color, 20%); 173 | font-size: @baseline * 0.8; 174 | } 175 | 176 | blockquote{ 177 | border-left: 5px solid fade(#000, 10%); 178 | .mhs; 179 | .pls; 180 | color: lighten(@body-color, 20%); 181 | font-size: @baseline * 0.8; 182 | font-style: italic; 183 | 184 | :last-child{ 185 | .mbn; 186 | } 187 | small{ 188 | color: fade(#000, 50%); 189 | font-size: @baseline * 0.7; 190 | font-style: normal; 191 | } 192 | } 193 | pre, code, kbd{ 194 | background: #F8F8F8; 195 | border: 1px solid #EAEAEA; 196 | .border-radius(3px); 197 | margin: 0 2px; 198 | padding: 0 5px; 199 | font-family: Consolas, "Courier New", Courier, mono; 200 | font-size: @body-font-size * 0.9; 201 | color: @body-color; 202 | word-wrap: break-word; 203 | } 204 | pre{ 205 | .mhs; 206 | .pah; 207 | 208 | code{ 209 | border: none; 210 | .man; 211 | .pan; 212 | } 213 | } 214 | a code{ 215 | background: none; 216 | border: none; 217 | .pan; 218 | .man; 219 | } 220 | strong{ 221 | font-weight: bold; 222 | } 223 | em{ 224 | font-style: italic; 225 | } 226 | ol, ul, dl{ 227 | .mls; 228 | .pls; 229 | ol,ul { 230 | .mbn; 231 | } 232 | } 233 | dt{ 234 | font-weight: bold; 235 | } 236 | dd{ 237 | .mls; 238 | } 239 | 240 | .table{ 241 | border-collapse: collapse; 242 | border-spacing: 0; 243 | width: 100%; 244 | } 245 | .table th, .table td{ 246 | border-top: 1px solid fade(#000, 10%); 247 | padding: 8px; 248 | text-align: left; 249 | } 250 | .table thead th{ 251 | vertical-align: bottom; 252 | font-weight: bold; 253 | } 254 | .table thead tr:first-child th{ 255 | border-top: none; 256 | } 257 | .table-bordered{ 258 | border: 1px solid fade(#000, 10%); 259 | border-collapse: separate; 260 | border-left: none; 261 | .border-radius(4px); 262 | 263 | th, td{ 264 | border-left: 1px solid fade(#000, 10%); 265 | } 266 | thead:last-child tr:last-child th:first-child, 267 | tbody:last-child tr:last-child td:first-child{ 268 | .border-radius(0 0 0 4px); 269 | } 270 | } 271 | .table-striped{ 272 | tbody tr:nth-child(odd) td{ 273 | background: fade(@body-accent-color, 4%); 274 | } 275 | } 276 | 277 | /* ========================================================================== 278 | Alerts 279 | ========================================================================== */ 280 | 281 | .alert{ 282 | background: #FCF8E3; 283 | border: 1px solid #FBEED5; 284 | .border-radius(4px); 285 | .mbs; 286 | .pah; 287 | color: #C09853; 288 | } 289 | .alert-success{ 290 | .alert; 291 | background-color: #DFF0D8; 292 | border-color: #D6E9C6; 293 | color: #468847; 294 | } 295 | .alert-error{ 296 | .alert; 297 | background-color: #F2DEDE; 298 | border-color: #EED3D7; 299 | color: #B94A48; 300 | } 301 | .alert-info{ 302 | .alert; 303 | background-color: #D9EDF7; 304 | border-color: #BCE8F1; 305 | color: #3A87AD; 306 | } 307 | 308 | /* ========================================================================== 309 | Forms 310 | ========================================================================== */ 311 | 312 | label{ 313 | display: block; 314 | font-weight: bold; 315 | 316 | .req{ 317 | color: @body-accent-color; 318 | font-weight: bold; 319 | } 320 | } 321 | input.text, 322 | textarea, 323 | select, 324 | .radio-group, 325 | .checkbox-group{ 326 | .mbs; 327 | } 328 | input.text, textarea{ 329 | .no-form; 330 | background: #EEE; 331 | border: 1px solid #CCC; 332 | .border-box; 333 | .border-radius(4px); 334 | .inner-shadow(fade(#000, 10%) 0 1px 3px); 335 | height: @baseline * 1.5; 336 | .pah; 337 | width: 100%; 338 | } 339 | textarea{ 340 | height: @baseline * 6; 341 | } 342 | select{ 343 | min-width: 30%; 344 | } 345 | .checkbox-group label, 346 | .radio-group label{ 347 | font-weight: normal; 348 | } 349 | .error{ 350 | background-color: #F2DEDE !important; 351 | border-color: red !important; 352 | outline-color: red !important; 353 | color: red !important; 354 | } 355 | -------------------------------------------------------------------------------- /styles/base/variables.less: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | Spacing 3 | ========================================================================== */ 4 | 5 | @baseline: 20px; 6 | 7 | /* ========================================================================== 8 | Typography 9 | ========================================================================== */ 10 | 11 | @body-color: #555; 12 | @body-font-family: "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, "Lucida Grande", sans-serif; 13 | @body-font-size: 14px; 14 | @body-accent-color: #f00; 15 | 16 | @header-color: #000; 17 | @header-font-family: "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, "Lucida Grande", sans-serif; 18 | @header-font-weight: bold; 19 | 20 | /* ========================================================================== 21 | Grid 22 | ========================================================================== */ 23 | 24 | @fixed-column-width: 40px; 25 | @fixed-gutter-width: 20px; 26 | @fixed-columns: 12; 27 | 28 | @fluid-column-width: 4.3%; 29 | @fluid-gutter-width: 4.4%; 30 | @fluid-columns: 12; 31 | 32 | @mobile-break-width: 480px; 33 | @mobile-column-width: 20%; 34 | @mobile-gutter-width: 6.6666%; 35 | @mobile-columns: 4; 36 | 37 | /* ========================================================================== 38 | Buttons 39 | ========================================================================== */ 40 | 41 | @button-color: #DDD; 42 | @button-primary-color: #0055CC; 43 | @button-info-color: #2F96B4; 44 | @button-success-color: #51A351; 45 | @button-warning-color: #FAA732; 46 | @button-danger-color: #BD362F; 47 | 48 | /* ========================================================================== 49 | Site Variables 50 | ========================================================================== */ 51 | 52 | @import "../site/variables"; -------------------------------------------------------------------------------- /styles/print.css: -------------------------------------------------------------------------------- 1 | body, .site-title, h1, h2, h3{ 2 | font-family: 'Georgia' !important; 3 | } 4 | 5 | 6 | nav.site-navigation, a.fork-me, a.top{ 7 | display:none; 8 | } 9 | 10 | div.site-content{ 11 | padding: 20px 40px 40px 20px; 12 | } -------------------------------------------------------------------------------- /styles/site/site-content.less: -------------------------------------------------------------------------------- 1 | .site-content{ 2 | padding: 20px 40px 40px 320px; 3 | 4 | h1{ 5 | clear: both; 6 | } 7 | } 8 | .interior-site-content{ 9 | .pad; 10 | } 11 | .top{ 12 | background: #333; 13 | display: inline-block; 14 | float: right; 15 | margin-right: -40px; 16 | padding: 4px 8px; 17 | color: #FFF; 18 | font-size: 12px; 19 | text-decoration: none !important; 20 | } 21 | -------------------------------------------------------------------------------- /styles/site/site-footer.less: -------------------------------------------------------------------------------- 1 | .site-footer{ 2 | clear: both; 3 | .ptd; 4 | font-size: 13px; 5 | text-align: center; 6 | } 7 | .site-footer img{ 8 | margin-left: 2px; 9 | position: relative; 10 | top: -2px; 11 | vertical-align: middle; 12 | } 13 | .site-footer ul{ 14 | list-style: none; 15 | .mhn; 16 | .phn; 17 | } 18 | -------------------------------------------------------------------------------- /styles/site/site-header.less: -------------------------------------------------------------------------------- 1 | .site-header{ 2 | position: relative; 3 | z-index: 1; 4 | text-align: center; 5 | } 6 | .site-title{ 7 | .mbn; 8 | font-family: 'Alfa Slab One'; 9 | font-size: @baseline * 4; 10 | font-weight: normal !important; 11 | line-height: @baseline * 5 !important; 12 | 13 | a{ 14 | text-decoration: none; 15 | } 16 | } 17 | .site-slogan{ 18 | font-weight: normal; 19 | } 20 | .fork-me, .fork-me img{ 21 | position: absolute; 22 | top: 0; 23 | right: 0; 24 | } 25 | .fork-me{ 26 | z-index: 2; 27 | } 28 | 29 | .interior-site-header{ 30 | background: #EEE; 31 | .inner-shadow(fade(#000, 7%) 0 0 40px); 32 | .pas; 33 | text-align: center; 34 | 35 | .site-title{ 36 | font-size: @baseline * 2; 37 | line-height: @baseline * 2 !important; 38 | } 39 | .site-slogan{ 40 | .mbs; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /styles/site/site-navigation.less: -------------------------------------------------------------------------------- 1 | .site-navigation{ 2 | background: #EEE; 3 | .inner-shadow(fade(#000, 7%) 0 0 40px); 4 | .pas; 5 | position: fixed; 6 | top: 0; 7 | bottom: 0; 8 | overflow: auto; 9 | width: 240px; 10 | } 11 | .build-date{ 12 | .mbs; 13 | color: #AAA; 14 | font-family: Helvetica, Arial, sans-serif; 15 | font-size: 11px; 16 | } 17 | .site-navigation ul{ 18 | .man; 19 | .pan; 20 | .no-list; 21 | font-size: 16px; 22 | margin-bottom: 10px; 23 | } 24 | .site-navigation > ul > li{ 25 | margin-bottom: 10px; 26 | } 27 | .site-navigation a{ 28 | text-decoration: underline; 29 | 30 | &:hover{ 31 | text-decoration: none; 32 | } 33 | } 34 | .site-navigation a.active{ 35 | background-color: #ff9; 36 | } 37 | .site-navigation ul ul{ 38 | .mls; 39 | .pth; 40 | font-size: 12px; 41 | 42 | a{ 43 | text-decoration: none; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /styles/site/variables.less: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | Spacing 3 | ========================================================================== */ 4 | 5 | @baseline: 20px; 6 | 7 | /* ========================================================================== 8 | Typography 9 | ========================================================================== */ 10 | 11 | @body-color: #666; 12 | @body-font-family: "Droid Serif", Georgia, "Times New Roman", Times, serif; 13 | @body-font-size: 14px; 14 | @body-accent-color: #000; 15 | 16 | @header-color: #111; 17 | @header-font-family: "Droid Serif", Georgia, "Times New Roman", Times, serif; 18 | @header-font-weight: 700; 19 | 20 | /* ========================================================================== 21 | Grid 22 | ========================================================================== */ 23 | 24 | @fixed-column-width: 60px; 25 | @fixed-gutter-width: 20px; 26 | @fixed-columns: 12; 27 | 28 | @fluid-column-width: 4.3%; 29 | @fluid-gutter-width: 4.4%; 30 | @fluid-columns: 12; 31 | 32 | @mobile-break-width: 480px; 33 | @mobile-column-width: 20%; 34 | @mobile-gutter-width: 6.6666%; 35 | @mobile-columns: 4; 36 | 37 | /* ========================================================================== 38 | Buttons 39 | ========================================================================== */ 40 | 41 | @button-color: #DDD; 42 | @button-primary-color: #0055CC; 43 | @button-info-color: #2F96B4; 44 | @button-success-color: #51A351; 45 | @button-warning-color: #FAA732; 46 | @button-danger-color: #BD362F; 47 | -------------------------------------------------------------------------------- /styles/syntax.css: -------------------------------------------------------------------------------- 1 | .highlight { background: #ffffff; margin: 0 4px; font-size: 0.8em; } 2 | .highlight .c { color: #999988; font-style: italic } /* Comment */ 3 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ 4 | .highlight .k { font-weight: bold } /* Keyword */ 5 | .highlight .o { font-weight: bold } /* Operator */ 6 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ 7 | .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ 8 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ 9 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ 10 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ 11 | .highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #aa0000 } /* Generic.Error */ 14 | .highlight .gh { color: #999999 } /* Generic.Heading */ 15 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ 16 | .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ 17 | .highlight .go { color: #888888 } /* Generic.Output */ 18 | .highlight .gp { color: #555555 } /* Generic.Prompt */ 19 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 20 | .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ 21 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */ 22 | .highlight .kc { font-weight: bold } /* Keyword.Constant */ 23 | .highlight .kd { font-weight: bold } /* Keyword.Declaration */ 24 | .highlight .kp { font-weight: bold } /* Keyword.Pseudo */ 25 | .highlight .kr { font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ 27 | .highlight .m { color: #009999 } /* Literal.Number */ 28 | .highlight .s { color: #d14 } /* Literal.String */ 29 | .highlight .na { color: #008080 } /* Name.Attribute */ 30 | .highlight .nb { color: #0086B3 } /* Name.Builtin */ 31 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #008080 } /* Name.Constant */ 33 | .highlight .ni { color: #800080 } /* Name.Entity */ 34 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ 35 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ 36 | .highlight .nn { color: #555555 } /* Name.Namespace */ 37 | .highlight .nt { color: #000080 } /* Name.Tag */ 38 | .highlight .nv { color: #008080 } /* Name.Variable */ 39 | .highlight .ow { font-weight: bold } /* Operator.Word */ 40 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 41 | .highlight .mf { color: #009999 } /* Literal.Number.Float */ 42 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */ 43 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */ 44 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */ 45 | .highlight .sb { color: #d14 } /* Literal.String.Backtick */ 46 | .highlight .sc { color: #d14 } /* Literal.String.Char */ 47 | .highlight .sd { color: #d14 } /* Literal.String.Doc */ 48 | .highlight .s2 { color: #d14 } /* Literal.String.Double */ 49 | .highlight .se { color: #d14 } /* Literal.String.Escape */ 50 | .highlight .sh { color: #d14 } /* Literal.String.Heredoc */ 51 | .highlight .si { color: #d14 } /* Literal.String.Interpol */ 52 | .highlight .sx { color: #d14 } /* Literal.String.Other */ 53 | .highlight .sr { color: #009926 } /* Literal.String.Regex */ 54 | .highlight .s1 { color: #d14 } /* Literal.String.Single */ 55 | .highlight .ss { color: #990073 } /* Literal.String.Symbol */ 56 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ 57 | .highlight .vc { color: #008080 } /* Name.Variable.Class */ 58 | .highlight .vg { color: #008080 } /* Name.Variable.Global */ 59 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */ 60 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ --------------------------------------------------------------------------------