├── .gitignore ├── CONTRIBUTING.md ├── 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-Vagrant.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 ├── 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-Interacting-via-Code.md ├── 07-03-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-Caching.md ├── 13-02-01-Bytecode-Cache.md ├── 13-03-01-Object-Caching.md ├── 14-01-01-Resources.md ├── 14-02-01-Frameworks.md ├── 14-03-01-Components.md ├── 14-04-01-Books.md ├── 15-01-01-Community.md └── 16-01-01-PHPDoc.md ├── banners.md ├── 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-logo.png ├── index.html ├── pages ├── Design-Patterns.md ├── Functional-Programming.md ├── The-Basics.md └── example.md ├── scripts └── setup.js ├── sitemap.xml └── 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 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to PHP The Right Way 2 | 3 | Enjoy [PHP The Right Way](http://laravel-taiwan.github.io/php-the-right-way/) 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/laravel-taiwan/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](http://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 this process is the best way to get your work 37 | included in the project: 38 | 39 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 40 | 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/laravel-taiwan/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/mojombo/jekyll/) gem to preview locally. 66 | 67 | 5. Commit your changes in logical chunks. Please adhere to these [git commit 68 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 69 | or your content is unlikely be merged into the main project. Use Git's 70 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 71 | feature to tidy up your commits before making them public. 72 | 73 | 6. Locally merge (or rebase) the upstream development branch into your topic branch: 74 | 75 | ```bash 76 | git pull [--rebase] upstream gh-pages 77 | ``` 78 | 79 | 7. Push your topic branch up to your fork: 80 | 81 | ```bash 82 | git push origin 83 | ``` 84 | 85 | 8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 86 | with a clear title and description. 87 | 88 | 89 | ## Contribution Agreement and Usage 90 | 91 | By submitting a pull request to this repository, you agree to allow the project 92 | owners to license your work under the the terms of the [Creative Commons Attribution-NonCommercial-ShareAlike 93 | 3.0 Unported License](http://creativecommons.org/licenses/by-nc-sa/3.0/). 94 | 95 | The same content and license will be used for all PHP The Right Way publications, 96 | including - but not limited to: 97 | 98 | * [phptherightway.com](http://phptherightway.com) 99 | * Translations of phptherightway.com 100 | * [LeanPub: PHP The Right Way](https://leanpub.com/phptherightway/) 101 | * Translations of "LeanPub: PHP The Right Way" 102 | 103 | All content is completely free now, and always will be. 104 | 105 | ## Contributor Style Guide 106 | 107 | 1. Use American English spelling (*primary English repo only*) 108 | 2. Use four (4) spaces to indent text; do not use tabs 109 | 3. Wrap all text to 120 characters 110 | 4. Code samples should adhere to PSR-1 or higher 111 | 5. Use [GitHub Flavored Markdown](http://github.github.com/github-flavored-markdown/) for all content 112 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Josh Lockhart 2 | 3 | http://creativecommons.org/licenses/by-nc-sa/3.0/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP 之道 2 | 3 | 翻譯完成文件: http://laravel-taiwan.github.io/php-the-right-way/ 4 | 5 | ## 概覽 6 | 7 | 這是 _PHP 之道_ 專案的 GitHub Pages 源庫。 8 | 9 | * 這個網站是 Jekyll 專案。 10 | * 每個區塊及子區塊都是在 `_posts/` 下的 Markdown 檔。 11 | * 子區塊在他們前面的有 `isChild: true` 宣告。 12 | * 導航列與頁面架構都是自動產生。 13 | 14 | ## 口耳相傳 15 | 16 | 您可以在網站上放置 _PHP 之道_ 的廣告橫幅。謝謝您的支持,讓 PHP 新手知道哪裡可以找到好的資訊! 17 | 18 | [廣告橫幅](http://laravel-taiwan.github.io/php-the-right-way/banners.html) 19 | 20 | ## 如何貢獻 21 | 22 | 1. Fork 然後編輯。 23 | 2. 安裝 [Ruby](https://rvm.io/rvm/install/) 和 [Jekyll](https://github.com/mojombo/jekyll/) 在本地端預覽。 24 | 3. 送出 Pull Request 供審閱。 25 | 26 | ### 貢獻者風格指南 27 | 28 | 1. 使用美式英文拼法(*僅適用於英文 repo*)。 29 | 2. 以 4 個空白縮進文字,請勿使用 Tab。 30 | 3. 每 120 字元請換行。 31 | 4. 範例程式請遵照 PSR-1 以上的標準。 32 | 5. 繁體中文翻譯請參考[中文文案排版指北](https://github.com/sparanoid/chinese-copywriting-guidelines)。 33 | 34 | ## 哪裡取得 35 | 36 | 37 | 38 | * [English](http://www.phptherightway.com) 39 | * [Deutsch](http://rwetzlmayr.github.io/php-the-right-way) 40 | * [Español](http://phpdevenezuela.github.io/php-the-right-way) 41 | * [Français](http://eilgin.github.io/php-the-right-way/) 42 | * [Indonesia](http://id.phptherightway.com) 43 | * [Italiano](http://it.phptherightway.com) 44 | * [Polski](http://pl.phptherightway.com) 45 | * [Português do Brasil](http://br.phptherightway.com) 46 | * [Română](https://bgui.github.io/php-the-right-way/) 47 | * [Slovenščina](http://sl.phptherightway.com) 48 | * [Srpski](http://phpsrbija.github.io/php-the-right-way/) 49 | * [Türkçe](http://hkulekci.github.io/php-the-right-way/) 50 | * [български](http://bg.phptherightway.com) 51 | * [Русский язык](http://getjump.github.io/ru-php-the-right-way) 52 | * [Українська](http://iflista.github.com/php-the-right-way) 53 | * [العربية](https://adaroobi.github.io/php-the-right-way/) 54 | * [فارسى](http://novid.github.io/php-the-right-way/) 55 | * [ภาษาไทย](https://apzentral.github.io/php-the-right-way/) 56 | * [한국어판](http://modernpug.github.io/php-the-right-way) 57 | * [日本語](http://ja.phptherightway.com) 58 | * [简体中文](http://laravel-china.github.io/php-the-right-way/) 59 | * [繁體中文](http://laravel-taiwan.github.io/php-the-right-way) 60 | 61 | ### 翻譯 62 | 63 | 如果您有興趣協助翻譯 _PHP 之道_,請在 GitHub 上 fork 此專案源庫並發布您的在地化版本於您的 GiHub Pages 帳號上。我們將會從主文件中連結您的翻譯版本。 64 | 65 | 為避免文件散亂及讀者混亂,請選擇下面一種方式: 66 | 67 | 1. 我們以 `[username].github.io/php-the-right-way` 連結您的 GitHub Pages。 68 | 2. 我們以子網域(如:"ru.phptherightway.com")的方式連結您的 GitHub Pages。 69 | 70 | 如果你選擇使用子網域,請在 `CNAME` 檔案中輸入您的子網域,並且告知我們為您設定網址。如果您不想使用子網域,請完整移除 `CNAME` 檔案,那麼在推送的過程中將不會被建立。 71 | 72 | 當您的翻譯作業完成,請在 Issue Tracker 裡開啟一個 issue 讓我們知道。 73 | 74 | ## 為何有此專案 75 | 76 | 最近在 PHP 社群中有大量的討論,對於 PHP 新手而言,缺少足夠且可靠的資訊。而此源庫就是為了解決此問題。 77 | 78 | ## 誰創立此專案 79 | 80 | 我的名字是 [Josh Lockhart](http://twitter.com/codeguy)。我是 [Slim Framework](http://www.slimframework.com/) 的作者並且在 [New Media Campaigns](http://www.newmediacampaigns.com/) 工作。 81 | 82 | ### 合作夥伴 83 | 84 | * [Kris Jordan](http://krisjordan.com/) 85 | * [Phil Sturgeon](http://philsturgeon.co.uk/) 86 | 87 | ## License 88 | 89 | [Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-nc-sa/3.0/) 90 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | safe: true 2 | baseurl: /php-the-right-way/ 3 | url: http://localhost:4000 4 | 5 | pygments: true 6 | markdown: kramdown 7 | permalink: date 8 | maruku: 9 | use_tex: false 10 | use_divs: false 11 | png_engine: blahtex 12 | png_dir: images/latex 13 | png_url: /images/latex 14 | -------------------------------------------------------------------------------- /_includes/welcome.md: -------------------------------------------------------------------------------- 1 | # 歡迎 {#site-header} 2 | 3 | 目前網路上充斥著大量的過時資訊,讓 PHP 新手誤入歧途,並且傳播著錯誤的實踐方法與不安全的代碼。 4 | _PHP 之道_ 是一個快速易讀的指南,包含 PHP 熱門編碼標準、連結網路上權威教程和現時貢獻者認定的最佳實踐方法。 5 | 6 | _使用 PHP 沒有規範化的方式_。本網站主要是協助 PHP 新手在他們沒有發現或者是太晚發現的主題, 7 | 或是經驗豐富的專業人士已經積習已久的做法提供一些新想法。 8 | 本網站也不會告訴您應該使用什麼樣的工具,而是提供多種選擇的建議,並盡可能地說明方法及用法上的差異。 9 | 10 | 當有更多有用的資訊以及範例時,此文件會隨著相關技術發展持續更新。 11 | 12 | 13 | ## 翻譯 {#translations} 14 | 15 | _PHP 之道_ 已經(或即將)翻譯成多種語言: 16 | 17 | * [English](http://www.phptherightway.com) 18 | * [Deutsch](http://rwetzlmayr.github.io/php-the-right-way) 19 | * [Español](http://phpdevenezuela.github.io/php-the-right-way) 20 | * [Français](http://eilgin.github.io/php-the-right-way/) 21 | * [Indonesia](http://id.phptherightway.com) 22 | * [Italiano](http://it.phptherightway.com) 23 | * [Polski](http://pl.phptherightway.com) 24 | * [Português do Brasil](http://br.phptherightway.com) 25 | * [Română](https://bgui.github.io/php-the-right-way/) 26 | * [Slovenščina](http://sl.phptherightway.com) 27 | * [Srpski](http://phpsrbija.github.io/php-the-right-way/) 28 | * [Türkçe](http://hkulekci.github.io/php-the-right-way/) 29 | * [български](http://bg.phptherightway.com) 30 | * [Русский язык](http://getjump.github.io/ru-php-the-right-way) 31 | * [Українська](http://iflista.github.com/php-the-right-way) 32 | * [العربية](https://adaroobi.github.io/php-the-right-way/) 33 | * [فارسى](http://novid.github.io/php-the-right-way/) 34 | * [ภาษาไทย](https://apzentral.github.io/php-the-right-way/) 35 | * [한국어판](http://modernpug.github.io/php-the-right-way) 36 | * [日本語](http://ja.phptherightway.com) 37 | * [简体中文](http://laravel-china.github.io/php-the-right-way/) 38 | * [繁體中文](http://laravel-taiwan.github.io/php-the-right-way) 39 | 40 | ## 如何貢獻 {#how-to-contribute} 41 | 42 | 幫助我們讓本網站作為 PHP 新手的最佳資源![在 GitHub 上貢獻][1] 43 | 44 | ## 口耳相傳 {#spread-the-word} 45 | 46 | 您可以在網站上放置 PHP 之道 的廣告橫幅。謝謝您的支持,並讓 PHP 新手能夠知道哪裡可以找到好的資訊! 47 | 48 | [廣告橫幅][2] 49 | 50 | [1]: https://github.com/laravel-taiwan/php-the-right-way 51 | [2]: /php-the-right-way/banners.html 52 | -------------------------------------------------------------------------------- /_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% if page.title %}{{ page.title }} - {% endif %}PHP: The Right Way 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 30 | 31 | 32 | 65 |
66 | 67 | 68 | Fork me on GitHub 69 | 70 | 71 | 78 | {{ content }} 79 | 102 |
103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /_layouts/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% if page.title %}{{ page.title }} - {% endif %}PHP: The Right Way 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 29 | 30 | 31 |
32 | 33 |
The Right Way.
34 | Return to Main Page 35 | 36 | Fork me on GitHub 37 | 38 |
39 |
40 |
You are reading extended content about…
41 | {{ content }} 42 | Return to Main Page 43 | 48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /_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: 使用最新穩定版本 (5.6) 3 | isChild: true 4 | anchor: use_the_current_stable_version 5 | --- 6 | 7 | ## 使用最新穩定版本 (5.6) {#use_the_current_stable_version_title} 8 | 9 | 如果你剛開始學習 PHP,請使用最新的穩定版本 [PHP 5.6][php-release]。PHP 近年來有巨大的改進,增加了許多強大的 [新特性](#language_highlights)。雖然 5.2 和 5.6 之間增加的版本號似乎很小,但他代表了 _重大的_ 改進。如果你想查詢一個函數的使用方法,在 [php.net][php-docs] 官方網站的 PHP 手冊上查詢。 10 | 11 | [php-release]: http://www.php.net/downloads.php 12 | [php-docs]: http://www.php.net/manual/en/ 13 | -------------------------------------------------------------------------------- /_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 5.4 之後,你可以開始學習 PHP 而不需要安裝與設定一個完整的網頁伺服器。要啟動內建伺服器,從終端機進入專案的 Web 根目錄下,執行下面的指令: 10 | 11 | > php -S localhost:8000 12 | 13 | * [瞭解更多內建的命令列伺服器][cli-server] 14 | 15 | [cli-server]: http://www.php.net/manual/en/features.commandline.webserver.php 16 | -------------------------------------------------------------------------------- /_posts/01-04-01-Mac-Setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: Mac 安裝 4 | anchor: mac_setup 5 | --- 6 | 7 | ## Mac 安裝 {#mac_setup_title} 8 | 9 | OSX 會預載 PHP 在系統中,但版本略舊於最新的穩定版本。Lion 預載 PHP 5.3.6,Mountain Lion 是 5.3.10,而 Mavericks 則是 5.4.17,Yosemite 則是 5.5.9,但在 PHP 5.6 出來之後,這些往往是不夠的。 10 | 11 | 這裏有許多方式可以在 OS X 上安裝 PHP。 12 | 13 | ### 透過 Homebrew 安裝 PHP 14 | 15 | [Homebrew](http://brew.sh/) 是一個強大的 OS X 專用套件管理系統,它可以讓您輕鬆地安裝 PHP 和各種擴充套件(extensions)。 16 | 17 | [Homebrew PHP] 是一個包含與PHP相關的Formulae,能讓您透過 homebrew 安裝PHP。也就是說,你可以透過 `brew install`指令,安裝 `php53`、`php54`、`php55`、或 `php56`,並且透過修改你的路徑 `PATH` 變數去切換各種版本。 18 | 19 | ### 透過 phpbrew 安裝 PHP 20 | 21 | [phpbrew] 是一個專門安裝與管理多重 PHP 版本的工具。它在應用程式或專案 PHP 版本需求不同的情況下會非常有用,讓你不再需要使用虛擬機器處理這種情況。 22 | 23 | ### 編譯原始碼 24 | 25 | 另一個可以讓你控制你要安裝的PHP版本的選擇就是[自行編譯][mac-compile]。在這種方法,您必須先確認您是否已經透過「Apple Developer: Mac Dev Center」下載、安裝 Xcode 或是 ["Command Line Tools for XCode"]。 26 | 27 | ### 整合包 (All-in-One Installers) 28 | 29 | 上述所列的解決方式主要是針對 PHP 本身,並且不包含:像是 Apach 、 Nginx 、或是 SQL 伺服器。整合包像是 [MAMP][mamp-downloads] 和 [XAMPP][xampp] 會幫你安裝這些軟體並且將他們綁在一起,但是易於安裝的背後就是犧牲了一些彈性。 30 | 31 | [Homebrew]: http://brew.sh/ 32 | [Homebrew PHP]: https://github.com/Homebrew/homebrew-php#installation 33 | [mac-compile]: http://www.php.net/manual/en/install.macosx.compile.php 34 | [xcode-gcc-substitution]: https://github.com/kennethreitz/osx-gcc-installer 35 | ["Command Line Tools for XCode"]: https://developer.apple.com/downloads 36 | [mamp-downloads]: http://www.mamp.info/en/downloads/ 37 | [phpbrew]: https://github.com/phpbrew/phpbrew 38 | [xampp]: http://www.apachefriends.org/en/xampp.html 39 | -------------------------------------------------------------------------------- /_posts/01-05-01-Windows-Setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: Windows 安裝 4 | anchor: windows_setup 5 | --- 6 | 7 | ## Windows 安裝 {#windows_setup_title} 8 | 9 | 在 Windows 下有多種安裝 PHP 的方式,你可以 [下載安裝檔][php-downloads] 並使用 `.msi` 的安裝檔。從 PHP 5.3.0 之後,該安裝檔將不再提供下載與支援。 10 | 11 | 如果是為了學習或者是本地端開發需求,從 PHP 5.4 之後,你可以使用內建的網頁伺服器,省去設定伺服器的麻煩。如果你想要也包含網頁伺服器以及 MySQL 的一鍵安裝包,那像是 [網頁平台安裝包][wpi] 的工具,如 [Zend Server CE][zsce]、[XAMPP][xampp]、[EasyPHP][easyphp] 和 [WAMP][wamp] 將會幫助您快速建立 Windows 開發環境。不過這些工具將會與正式執行環境會有些許差別,如果你是在 Windows 開發,而部署至 Linux 上的情況下,請小心。 12 | 13 | 如果你希望將網頁部署到 Windows 上,那 IIS7 將會提供最穩定且最佳的性能。你可以使用 「phpmanager][phpmanager] (IIS7 圖形化插件) 能讓你簡單設定並管理 PHP。IIS7 也有內建 FastCGI,你僅需將 PHP 設定為他的處理器即可。詳情請見 [dedicated area on iis.net][php-iis]。 14 | 15 | [php-downloads]: http://windows.php.net 16 | [phpmanager]: http://phpmanager.codeplex.com/ 17 | [wpi]: http://www.microsoft.com/web/downloads/platform.aspx 18 | [zsce]: http://www.zend.com/en/products/server-ce/ 19 | [xampp]: http://www.apachefriends.org/en/xampp.html 20 | [easyphp]: http://www.easyphp.org/ 21 | [wamp]: http://www.wampserver.com/ 22 | [php-iis]: http://php.iis.net/ 23 | -------------------------------------------------------------------------------- /_posts/01-06-01-Vagrant.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | anchor: vagrant 4 | --- 5 | 6 | ## Vagrant {#vagrant_title} 7 | 8 | 如果你在開發應用與發布應用時使用不同的環境,那麼在正式上線時,可能會發生一些奇怪的錯誤。 9 | 在與一群團隊的開發者共同開發時,要保持每個環境同步,且所有的函式庫皆在同版本上,是最棘手的一件事情。 10 | 11 | 如果你是在 Windows 上開發,部署到 Linux(或非 Windows 平台),或者是團隊裡工作,你應該考慮使用虛擬機器。聽起來很麻煩,但使用 [Vagrant][vagrant] 你僅需幾個步驟就能設定好一個簡單的虛擬機器。再來需要手動設定基礎環境,或者你可以使用 “部署軟體” 如 [Puppet][puppet] 或 [Chef][chef] 來幫你完成這些事情。部署基礎環境是很好的做法保證大家的開發環境以相同的方式建立。並且省去你維護那些複雜的”安裝指令”。你也可以輕易地毀掉現有的基礎環境後再重新建立一個新的,這樣你就可以有一個全新的環境。 12 | 13 | Vagrant 會建立共用資料夾,讓你可以在你的主機與虛擬機之間共用程式碼,也就是你可以在你的主機上建立與編輯檔案,然後在你的虛擬機器上面執行。 14 | 15 | 16 | ### 小助手 17 | 18 | 如果你想要取得一些 Vagrant 上的使用幫助的話,可以參考下面三個服務: 19 | 20 | - [Rove][rove]: 提供你預先建立的 Vagrant 類型,其中包含 PHP 選項。使用 Chef 來部署。 21 | - [Puphpet][puphpet]: 簡單的圖形化介面來設定虛擬機裡的 PHP 開發環境。**專注於 PHP 開發**,除了本地端虛擬機外,還可以部署到雲端服務上。部署工具使用 Puppet。 22 | - [Protobox][protobox]: 構建在 Vagrant 上的服務層,提供網頁圖形介面去設定虛擬機器。通過單一 YAML 檔案去控制虛擬機的所有設定。 23 | - [Phansible][phansible]: 為 PHP 專案提供一個簡單易用的介面,協助你產生 Ansible Playbooks。 24 | 25 | [vagrant]: http://vagrantup.com/ 26 | [puppet]: http://www.puppetlabs.com/ 27 | [chef]: http://www.opscode.com/ 28 | [rove]: http://rove.io/ 29 | [puphpet]: https://puphpet.com/ 30 | [protobox]: http://getprotobox.com/ 31 | [phansible]: http://phansible.com/ 32 | -------------------------------------------------------------------------------- /_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 社群百花齊放,擁有大量的函式庫、框架和元件。PHP 開發者通常會選擇多個外部套件並將它們整合進單一專案中。因此 PHP 程式碼遵循(盡可能接近)一個共通的程式碼風格就會非常重要,這讓開發者可以輕鬆地將多個外部套件整合在自己的專案當中。 9 | 10 | [Framework Interop Group][fig] 提出並通過了一系列的風格建議。其中有部分是關於程式碼風格的,即 [PSR-0][psr0]、[PSR-1][psr1]、[PSR-2][psr2] 和 [PSR-4][psr4]。這些建議是由一系列專案像是 Drupal, Zend, Symfony, CakePHP, phpBB, AWS SDK, FuelPHP, Lithium 所採用。你可以在您的專案中使用或者是繼續使用您自己的風格。 11 | 12 | 理想狀況下,你應該遵循一個已知的標準來撰寫 PHP 程式碼。可能是 PSR 的組合或者是 PEAR 或 Zend 程式碼標準的其中一個。這代表其他開發者能夠方便的閱讀與使用你的程式碼,且使用這些套件的應用程式能夠平順地與許多第三方套件一起使用。 13 | 14 | * [閱讀 PSR-0][psr0] 15 | * [閱讀 PSR-1][psr1] 16 | * [閱讀 PSR-2][psr2] 17 | * [閱讀 PSR-4][psr4] 18 | * [閱讀 PEAR 編碼準則][pear-cs] 19 | * [閱讀 Zend 編碼準則][zend-cs] 20 | * [閱讀 Symfony 編碼準則][symfony-cs] 21 | 22 | 你可以使用 [PHP_CodeSniffer][phpcs] 來檢查程式碼是否符合這些準則,而文字編輯器 [Sublime Text 2][st-cs] 的套件也可以提供即時檢查。 23 | 24 | 使用 Fabien Potencier 的 [PHP Codeing Standards Fixer][phpcsfixer] 可以自動修正你的程式碼語法,讓其符合標準,不用你自己手動修復。 25 | 26 | 所有的變數名稱和程式碼結構建議使用英文編寫。註解可以使用任何語言,只要讓現在及未來的夥伴能夠容易閱讀即可。 27 | 28 | [fig]: http://www.php-fig.org/ 29 | [psr0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md 30 | [psr1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md 31 | [psr2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md 32 | [psr4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md 33 | [pear-cs]: http://pear.php.net/manual/en/standards.php 34 | [zend-cs]: http://framework.zend.com/wiki/display/ZFDEV2/Coding+Standards 35 | [symfony-cs]: http://symfony.com/doc/current/contributing/code/standards.html 36 | [phpcs]: http://pear.php.net/package/PHP_CodeSniffer/ 37 | [st-cs]: https://github.com/benmatselby/sublime-phpcs 38 | [phpcsfixer]: http://cs.sensiolabs.org/ 39 | -------------------------------------------------------------------------------- /_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 | isChild: true 3 | title: 編程範式 4 | anchor: programming_paradigms 5 | --- 6 | 7 | ## 編程範式 {#programming_paradigms_title} 8 | 9 | PHP 是一個彈性的動態語言,支援多種編程技術。這幾年一直不斷的進化,重要的里程碑包含 PHP 5.0 (2004) 增加完善的物件導向模型,PHP 5.3 (2009) 增加匿名函式與命名空間以及 PHP 5.4 (2012) 增加的 traits。 10 | 11 | ### 物件導向編程 12 | 13 | PHP 擁有完整的物件導向編程的特性,包含支援類別、抽象類別、介面、繼承、構造函數、克隆和異常等。 14 | 15 | 16 | * [閱讀 PHP 物件導向][oop] 17 | * [閱讀 Traits][traits] 18 | 19 | ### 函式編程 20 | 21 | PHP 支援第一類函式(first-class function),即函式可以被賦值給一個變數,包括使用者自定義或者是內建函式,然後動態調用它。函式可以作為參數傳遞給其他函式(稱作高階函數),也可以做完函數返回值返回。 22 | 23 | PHP 支援遞迴,也就是函式可以呼叫自己,但多數 PHP 程式碼較常使用迭代。 24 | 25 | 自從 PHP 5.3 (2009) 之後開始引入支援閉包匿名函式。 26 | 27 | PHP 5.4 支援將閉包綁定到物件的作用域中,並改善其可調用性,如此即可在大部分狀況下使用匿名函式取代一般函式。 28 | 29 | * 學習更多 [PHP 函式編程](pages/Functional-Programming.html) 30 | * [閱讀匿名函式][anonymous-functions] 31 | * [閱讀閉包類別][closure-class] 32 | * [更多關於 Closures RFC][closures-rfc] 33 | * [閱讀 Callables][callables] 34 | * [閱讀動態調用函式 `call_user_func_array`][call-user-func-array] 35 | 36 | ### 元編程 37 | 38 | PHP 通過反射 API 和魔術方法機制,支援多種方式的元編程。開發者通過魔術方法,如 `__get()`, `__set()`, `__clone()`, `__toString()`, `__invoke()`, 等等,可以改變類別的行為。Ruby 開發者經常說 PHP 沒有 `method_missing` 方法,實際上通過 `__call()` 和 `__callStatic()` 就可以完成相同的功能。 39 | 40 | * [閱讀魔術方法][magic-methods] 41 | * [閱讀反射][reflection] 42 | 43 | [namespaces]: http://php.net/manual/en/language.namespaces.php 44 | [overloading]: http://php.net/manual/en/language.oop5.overloading.php 45 | [oop]: http://www.php.net/manual/en/language.oop5.php 46 | [anonymous-functions]: http://www.php.net/manual/en/functions.anonymous.php 47 | [closure-class]: http://php.net/manual/en/class.closure.php 48 | [callables]: http://php.net/manual/en/language.types.callable.php 49 | [magic-methods]: http://php.net/manual/en/language.oop5.magic.php 50 | [reflection]: http://www.php.net/manual/en/intro.reflection.php 51 | [traits]: http://www.php.net/traits 52 | [call-user-func-array]: http://php.net/manual/en/function.call-user-func-array.php 53 | [closures-rfc]: https://wiki.php.net/rfc/closures 54 | -------------------------------------------------------------------------------- /_posts/03-03-01-Namespaces.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 命名空間 4 | anchor: namespaces 5 | --- 6 | 7 | ## 命名空間 {#namespaces_title} 8 | 9 | 如前所述,PHP 社群裡許多開發者已經開發了大量的程式碼。這意味著一個函式庫的的 PHP 程式碼可能使用了另外一個函式庫中相同的類別名稱。如果他們使用同一個命名空間,那將會產生衝突導致異常。 10 | 11 | _命名空間_ 解決了這個問題。如 PHP 手冊裡所描述,命名空間類似作業系統中的目錄,兩個同名的文件可以共存於不同的目錄下。同理兩個同名的 PHP 類別可以在不同的 PHP 命名空間下共存,就這麼簡單。 12 | 13 | 因此把你的程式碼放在你的命名空間下就非常重要,避免其他開發者擔心與第三方函式庫衝突。 14 | 15 | [PSR-0][psr0] 提供了命名空間的推薦使用方式,它提供一個標準的文件、類別和命名空間的使用慣例,進而讓程式碼做到隨插即用。 16 | 17 | 2013 年 12 月,PHP-FIG 發布了新的自動加載標準:[PSR-4][psr4],期望將來替換 PSR-0 標準。但 PSR-4 要求 PHP 5.3 以上的版本,而許多專案都還是使用 PHP 5.2,所以目前兩個都能使用。如果你在新應用或套件中使用自動加載標準,應優先考慮使用 PSR-4。 18 | 19 | * [閱讀命名空間][namespaces] 20 | * [閱讀 PSR-0][psr0] 21 | * [閱讀 PSR-4][psr4] 22 | 23 | [namespaces]: http://php.net/manual/en/language.namespaces.php 24 | [psr0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md 25 | [psr4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md 26 | -------------------------------------------------------------------------------- /_posts/03-04-01-Standard-PHP-Library.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 標準 PHP 函式庫 4 | anchor: standard_php_library 5 | --- 6 | 7 | ## 標準 PHP 函式庫 {#standard_php_library_title} 8 | 9 | 標準 PHP 函式庫(SPL)隨著 PHP 一起發佈,提供了一組類別和介面。包含了常用的資料結構類別(堆疊、佇列、堆積等等),以及遊走這些資料結構的迭代器,或者你可以自己實作 SPL 介面。 10 | 11 | * [閱讀 SPL][spl] 12 | 13 | [spl]: http://php.net/manual/en/book.spl.php 14 | -------------------------------------------------------------------------------- /_posts/03-05-01-Command-Line-Interface.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 命令列介面 4 | anchor: command_line_interface 5 | --- 6 | 7 | ## 命令列介面 {#command_line_interface_title} 8 | 9 | PHP 主要是用來開發網頁應用程式,但用來開發命令列介面(CLI)程式也很好用。命令列程式可以幫你完成自動化的任務,如測試、部署和應用管理。 10 | 11 | CLI 程式很強大,因為你可以直接使用應用的程式碼而不需要為它建立網頁圖形化介面。唯一要注意的是,請不要將你的 CLI 程式放置在公開的網頁根目錄下。 12 | 13 | 試著在命令列下執行 PHP: 14 | 15 | {% highlight bash %} 16 | > php -i 17 | {% endhighlight %} 18 | 19 | 選項 `-i` 會像 [`phpinfo`][phpinfo] 函式一樣將你的 PHP 設定值顯示出來。 20 | 21 | 選項 `-a` 提供一個互動式介面,類似 ruby 的 IRB 或者是 python 的互動式介面。此外還有其他有用的 [命令列選項][cli-options]。 22 | 23 | 接下來,來寫一個簡單的 “Hello, $name” CLI 程式。首先建議一個名為 `hello.php` 的檔案如下。 24 | 25 | {% highlight php %} 26 | php hello.php 43 | Usage: php hello.php [name] 44 | > php hello.php world 45 | Hello, world 46 | {% endhighlight %} 47 | 48 | 49 | * [學習如何從命令列執行 PHP][php-cli] 50 | * [學習如何在 Windows 下從命令列執行 PHP][php-cli-windows] 51 | 52 | [phpinfo]: http://php.net/manual/en/function.phpinfo.php 53 | [cli-options]: http://www.php.net/manual/en/features.commandline.options.php 54 | [argc]: http://php.net/manual/en/reserved.variables.argc.php 55 | [argv]: http://php.net/manual/en/reserved.variables.argv.php 56 | [php-cli]: http://php.net/manual/en/features.commandline.php 57 | [php-cli-windows]: http://www.php.net/manual/en/install.windows.commandline.php 58 | [exit-codes]: http://www.gsp.com/cgi-bin/man.cgi?section=3&topic=sysexits 59 | -------------------------------------------------------------------------------- /_posts/03-06-01-XDebug.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: XDebug 4 | anchor: xdebug 5 | --- 6 | 7 | ## XDebug {#xdebug_title} 8 | 9 | 除錯器是軟體開發過程中一個相當有用的工具。透過它可以追蹤程式碼執行過程、查看堆疊資訊。XDebug 是一個 PHP 除錯器,能夠整合在多款 IDE 內,提供中斷點和查看堆疊訊息等功能,還可以和 PHPUnit 、 KCacheGrind 這類工具配合,提供計算程式碼涵蓋率與程式碼分析。 10 | 單單依靠 var_dump/print_r 來除錯的你, 是否經常處在困境中,除錯器將會是你的最佳救星,趕緊來試試看吧。 11 | 12 | [安裝 XDebug][xdebug-install] 有些麻煩,但其中一個重要功能"遠端除錯" ——如果你是在本機端進行開發,並在虛擬機器或其他主機上進行測試,那麼"遠端除錯"這個功能將對你非常有用。 13 | 14 | 通常,你需要修改 Apache VHost 或 .htaccess 檔案,加入以下設定: 15 | 16 | php_value xdebug.remote_host=192.168.?.? 17 | php_value xdebug.remote_port=9000 18 | 19 | "remote host" 和 "remote port" 將會對應本機位置與 IDE 監聽埠 20 | ,同時 IDE 開啟等待連接模式,並拜訪此網址: 21 | 22 | http://your-website.example.com/index.php?XDEBUG_SESSION_START=1 23 | 24 | 如此 IDE 就會監控腳本的執行,讓你可以設定中斷點和查看變數值。 25 | 26 | 圖形化的除錯器讓逐步檢視程式碼、查看變數與即時監看變得十分容易。許多 IDE 內建或是提供套件安裝 xdebug 除錯器。例如 MacGDBp 是一套 Mac 上免費、開源的軟體。 27 | 28 | * [瞭解更多 XDebug][xdebug-docs] 29 | * [瞭解更多 MacGDBp][macgdbp-install] 30 | 31 | [xdebug-docs]: http://xdebug.org/docs/ 32 | [xdebug-install]: http://xdebug.org/docs/install 33 | [macgdbp-install]: http://www.bluestatic.org/software/macgdbp/ 34 | -------------------------------------------------------------------------------- /_posts/04-01-01-Dependency-Management.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 相依管理 3 | anchor: dependency_management 4 | --- 5 | 6 | # 相依管理 {#dependency_management_title} 7 | 8 | PHP 有許多的函式庫、框架及元件可以給你選擇。你的專案將會有可能使用到它們 — 這就是專案套件相依管理 9 | 一直到最近,PHP並沒有一個好的方法來做專案的套件相依性管理,即使你手動的管理它們,你還是需要擔心自動讀取器。 10 | 11 | 現在PHP有二個主要的套件管理系統 - Composer 及 PEAR. 哪一個比較適合你? 答案是二者皆是 12 | 13 | * 使用 **Composer** 在單一個專案中管理許多獨立的套件。 14 | * 使用 **PEAR** 在你的系統中管理PHP的獨立套件。 15 | 16 | 一般來說,Composer 的套件只能在你的專案之中可以使用,反之一個 PEAR 的套件可以在你所有的 PHP 專案中使用。雖然 PEAR 可能聽起來比較簡單使用。但他們二者各有優點在不同的專案之中幫助你使用套件。 17 | -------------------------------------------------------------------------------- /_posts/04-02-01-Composer-and-Packagist.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: Composer 與 Packagist 4 | anchor: composer_and_packagist 5 | --- 6 | 7 | ## Composer 與 Packagist {#composer_and_packagist_title} 8 | 9 | Composer 是一個**傑出**的PHP套件管理程式。在 `composer.json` 檔案中寫入你專案所要使用的套件在打上一點簡單的指令,Composer 將會自動幫你下載並設定你專案中所需要的套件。 10 | 11 | 現在已經有許多 PHP 函式庫相容於 Composer,並且隨時都可以在你的專案中使用.這些"packages(套件)"都會列在 [Packagist][1],這是一個官方的 Composer 相容的套件倉庫。 12 | 13 | ### 如何安裝 Composer 14 | 15 | 你可以區域性的安裝 Composer (在你目前工作的目錄;這裡不是很推薦)或是全域(e.g. /usr/local/bin).我們假設你想安裝區域的 Composer.從你專案的根目錄輸入: 16 | 17 | curl -s https://getcomposer.org/installer | php 18 | 19 | 這串指令將會下載 `composer.phar` (一個PHP執行檔)。你可以使用 `php` 執行這個檔案用來管理你的專案套件。 20 | **註記:**假如你是直接下載程式來編譯,請先確認你下載編譯的檔案是否安全 21 | 22 | #### Windows環境下安裝 23 | 24 | 對於Windows的使用者而言最簡單取得及執行的方法就是使用 [ComposerSetup][6] 安裝檔,安裝檔會提供全域性的 composer 及設定你的 `$PATH`,所以你可以直接在命令視窗中的任何目錄下使用 `composer`。 25 | 26 | ### 如何手動安裝 Composer 27 | 28 | 要手動安裝Composer是一件較進階的技術;僅管如此還是有許多開發者有各種原因喜歡使用這種互動式的應用程式安裝 Composer。在安裝前請先確認你的PHP安裝項目如下: 29 | 30 | - 符合滿足Composer要求或以上的PHP版本 31 | - `.phar` 類型檔案可以正確的被執行 32 | - 相關的目錄權限有開放 33 | - 相關有問題的擴展套件沒有被讀取 34 | - 相關的 `php.ini` 設定已完成 35 | 36 | 由於手動安裝沒有做這些檢查,你必須自已衡量決定是否值得做這些事,以下是如何取得 Composer 手動安裝: 37 | 38 | curl -s https://getcomposer.org/composer.phar -o $HOME/local/bin/composer 39 | chmod +x $HOME/local/bin/composer 40 | 41 | 路徑 `$HOME/local/bin` (或是你選擇的路徑) 應該在你的 `$PATH` 環境變數中。這將會影響 `composer` 這個指令是否有效. 42 | 43 | 當你已經安裝到文件叫你執行 Composer 指令是 `php composer.phar install`時,你可以使用下列指令替代: 44 | 45 | composer install 46 | 47 | 本章節我們假設你已經全域性的安裝 Composer。 48 | 49 | ### 如何設定及安裝獨立套件 50 | 51 | Composer 會持續的追縱你專案的獨立套件透過一個叫 `composer.json` 的檔案。 如果你希望你可以自已管理這個檔案。或是使用Composer它他自已會管理。`composer require` 這個指令會新增一個專案獨立套件進去且如果你沒有 `composer.json` 這個檔案他會直接建立。這裡有個範例是為你的專案加入 [Twig][2]這個獨立套件。 52 | 53 | composer require twig/twig:~1.8 54 | 55 | 另外 `composer init` 這個指令將會指引你建立一個完整的 `composer.json` 檔案在你的專案之中。無論你使用哪種方式當你已經建立你的 `composer.json` 檔案,你可以告訴 Composer 去下載及安裝你的獨立套件進 `vendors/` 目錄中。這這指令也適用於所有你已經下載並已經提供一個 `composer.json` 的專案: 56 | 57 | composer install 58 | 59 | 接下來,新增這一行到你的應用程式中主要 PHP 檔案,這將會告訴 PHP 使用 Composer 的自動讀取器並在你的專案中使用獨立套件。 60 | 61 | {% highlight php %} 62 | pear-channel/Package 53 | 54 | "pear" 前綴是一件有點笨的寫程式方式來避免任何的版本衝突,因為一個 pear 的頻道可以允許有一樣的另外的其他套件名稱。舉例來說頻道的縮寫(或是完整網址)可以用來參考這個套件是屬於哪個頻道的。 55 | 56 | 當安裝這個程式碼後這將會安裝至你的 vendor 目錄並且透過 Composer 自動讀取器自動可以使用。 57 | 58 | > vendor/pear-pear2.php.net/PEAR2_HTTP_Request/pear2/HTTP/Request.php 59 | 60 | 要使用這個 PEAR 套件可以簡單的參考下面方式: 61 | 62 | {% highlight php %} 63 | $request = new pear2\HTTP\Request(); 64 | {% endhighlight %} 65 | 66 | * [了解更多關於透過 Composer 使用 PEAR][6] 67 | 68 | [1]: http://pear.php.net/ 69 | [2]: http://pear.php.net/manual/en/installation.getting.php 70 | [3]: http://pear.php.net/packages.php 71 | [4]: http://pear.php.net/manual/en/guide.users.commandline.channels.php 72 | [5]: /#composer_and_packagist 73 | [6]: http://getcomposer.org/doc/05-repositories.md#pear 74 | -------------------------------------------------------------------------------- /_posts/05-01-01-Coding-Practices.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 開發實踐 3 | anchor: coding_practices 4 | --- 5 | 6 | # 開發實踐 {#coding_practices_title} 7 | -------------------------------------------------------------------------------- /_posts/05-02-01-The-Basics.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 基礎知識 4 | anchor: the_basics 5 | --- 6 | 7 | ## 基礎知識 {#the_basics_title} 8 | 9 | PHP 是一種兼容並蓄的語言,讓各種程度的開發者都能快速且有效率的進行開發。然而,當我們在這個語言不斷向上成長的同時,往往會有個壞習慣抄捷徑(或略過)那些基礎,為了解決這個問題,這個章節用來重新檢視 PHP 的基礎開發實踐。 10 | 11 | * 繼續閱讀 [基礎知識](pages/The-Basics.html) 12 | -------------------------------------------------------------------------------- /_posts/05-03-01-Date-and-Time.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 日期與時間 4 | anchor: date_and_time 5 | --- 6 | 7 | ## 日期與時間 {#date_and_time_title} 8 | 9 | PHP 提供 DateTime 類別處理讀取、設定、比較和計算日期與時間。儘管 PHP 有許多日期與時間相關的函數,但是 DateTime 類別提供更佳的物件導向介面來處理常見的操作。並且能處理時區問題(礙於篇幅這裡並不多著墨)。 10 | 11 | 要使用 DateTime 類,可以使用工廠模式方法 `createFromFormat()` 來把原始的日期時間字串轉換成時間物件,或直接用 `new \DateTime` 取得當下日期時間的 DateTime 物件。可用 `format()` 方法轉換 DateTime 物件為字串作為輸出。 12 | 13 | {% highlight php %} 14 | format('m/d/Y') . "\n"; 19 | {% endhighlight %} 20 | 21 | DateTime 計算時間通常需要 DateInterval 類別,如 `add()` 和 `sub()` 方法,都是將時間差當作參數,而避免使用時間戳記來計算時間區間,這是因為當遇到日光節約或是時區問題都會造成錯誤。並且應該盡量使用 `diff()` 方法計算日期差,這會回傳一個新的 DateInterval 物件,非常容易作為輸出顯示使用。 22 | {% highlight php %} 23 | add(new \DateInterval('P1M6D')); 27 | 28 | $diff = $end->diff($start); 29 | echo 'Difference: ' . $diff->format('%m month, %d days (total: %a days)') . "\n"; 30 | // Difference: 1 month, 6 days (total: 37 days) 31 | {% endhighlight %} 32 | 33 | DateTime 物件之間可以直接比較: 34 | {% highlight php %} 35 | format('m/d/Y') . ' '; 50 | } 51 | {% endhighlight %} 52 | 53 | * [了解更多 DateTime][datetime] 54 | * [了解更多 date formatting][dateformat] (允許的日期格式字串) 55 | 56 | [datetime]: http://www.php.net/manual/book.datetime.php 57 | [dateformat]: http://www.php.net/manual/function.date.php 58 | -------------------------------------------------------------------------------- /_posts/05-04-01-Design-Patterns.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 設計模式 4 | anchor: design_patterns 5 | --- 6 | 7 | ## 設計模式 {#design_patterns_title} 8 | 9 | 在程式架構中使用常見的設計模式是很有用的,可以增加程式碼的易維護性,也讓其他開發者更容易理解這些程式碼。 10 | 11 | 如果你的專案有使用框架,那麼在程式碼與架構上都會遵循框架的規範,自然也繼承了框架中的各種模式。然而基於此之上你仍然可以挑選適合的模式來撰寫程式碼。反之,若沒有使用框架,那麼你必須挑選適合的類型與符合當下規模的較佳模式。 12 | 13 | * 更多[設計模式](pages/Design-Patterns.html)介紹 14 | -------------------------------------------------------------------------------- /_posts/05-05-01-PHP-and-UTF8.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Working with 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。而有許多方式可以確認 UTF-8 字串的處理是正確的,但通常不容易,還需要從上而下翻遍程序所有階層,從 HTML、SQL 到 PHP。我們將會聚焦在簡短的實踐總結。 15 | 16 | ### UTF-8 在 PHP 階層 17 | 18 | 基本的字串操作,像是連接兩個字串、賦值給變數,並不需要特別作 UTF-8 處理。然而,大部分字串函數,像是 `strpos()` 和 `strlen()` 則確實需要考慮。這些函數通常以 `mb_*` 作為開頭:像是 `mb_strpos()` 和 `mb_strlen()`。這類函數藉由[Multibyte String Extension]設計用來專門用來處理 Unicode 字串。 19 | 20 | 每次操作 Unicode 字串,都必須使用 `mb_*` 這類函數。例如,若你對 UTF-8 字串使用 `substr()` 有很大的機會結果會是包含半字元的亂碼,正確的方式是使用 `mb_substr()`。 21 | 22 | 困難的是,要記得每次都使用 `mb_*` 函數,萬一有個地方沒有用到,那麼你的 Unicode 字串就有可能在接下來的程序中被轉變成亂碼。 23 | 並不是所有的字串函數都有對應的 `mb_*` 函數,就算只有一處沒有正確轉換,都有可能會出現亂碼。 24 | 25 | 你應該在 PHP 程式的開頭使用 `mb_internal_encoding()` (或是在最上層的全域載入的地方使用),若是有輸出訊息到瀏覽器,則要再加上 `mb_http_output()` 函數。明確定義你的字串編碼可以幫你省下許多頭痛的時間。 26 | 27 | 此外,許多 PHP 字串函數都允許設定字元編碼,你應該總是設定成 UTF-8,例如,`htmlentities()` 函數。而在 PHP 5.4.0 之後已經將 UTF-8 作為 `htmlentities()` 和 `htmlspecialchars()` 預設編碼。 28 | 29 | 最後,如果你建立的是分散式架構程式,且不能確定 `mbstring` 函數是啟用狀態,那麼則建議使用 [patchwork/utf8] 這個 Composer 套件。它將會自動判斷該環境能否使用 `mbstring` 函數。 30 | 31 | [Multibyte String Extension]: http://php.net/manual/en/book.mbstring.php 32 | [patchwork/utf8]: https://packagist.org/packages/patchwork/utf8 33 | 34 | ### UTF-8 在資料庫階層 35 | 36 | 如果你是使用 PHP 來存取 MySQL,有可能會發生使用非 UTF-8 編碼來儲存資料,即使你有遵照上述的注意事項。 37 | 38 | 為了確保字串從 PHP 到 MySQL 都是 UTF-8 編碼,請確認資料庫和資料表都是設定為 `utf8mb4` 字元集,且使用 `utf8mb4` 作為 PDO 連線字串,請見以下範例,這將是非常重要的。 39 | 40 | 請注意,你必須使用 `utf8mb4` 字元集來作為完整的支援 UTF-8,而非使用 `utf8` 字元集!原因請參見瞭解更多。 41 | 42 | ### UTF-8 在瀏覽器階層 43 | 44 | 使用 `mb_http_output()` 函數來確保 PHP 輸出 UTF-8 字串給瀏覽器。 45 | 46 | 瀏覽器需要藉由 HTTP 回應來得知此頁面要被解析成 UTF-8 編碼,藉由歷史的方法來達到需要包含[字元集 `` 標籤](http://htmlpurifier.org/docs/enduser-utf8.html) 在頁面的 `` 標籤內。這種方式是非常有效的,但是設定這個字元集到 `Content-Type` 標頭實際上會[更快](https://developers.google.com/speed/docs/best-practices/rendering#SpecifyCharsetEarly)。 47 | 48 | {% highlight php %} 49 | \PDO::ERRMODE_EXCEPTION, 72 | \PDO::ATTR_PERSISTENT => false 73 | ) 74 | ); 75 | 76 | // 資料庫以 UTF-8 編碼儲存我們轉換的字串 77 | // 你的資料庫和資料表是設定成 utf8mb4 字元集,對吧? 78 | $handle = $link->prepare('insert into ElvishSentences (Id, Body) values (?, ?)'); 79 | $handle->bindValue(1, 1, PDO::PARAM_INT); 80 | $handle->bindValue(2, $string); 81 | $handle->execute(); 82 | 83 | // 取得我們剛才存入的字串,來驗證儲存正確。 84 | $handle = $link->prepare('select * from ElvishSentences where Id = ?'); 85 | $handle->bindValue(1, 1, PDO::PARAM_INT); 86 | $handle->execute(); 87 | 88 | // 將結果存入物件,待會要輸出到 HTML 89 | $result = $handle->fetchAll(\PDO::FETCH_OBJ); 90 | 91 | header('Content-Type: text/html; charset=UTF-8'); 92 | ?> 93 | 94 | 95 | 96 | UTF-8 test page 97 | 98 | 99 | Body); // This should correctly output our transformed UTF-8 string to the browser 102 | } 103 | ?> 104 | 105 | 106 | {% endhighlight %} 107 | 108 | ### 瞭解更多 109 | 110 | * [PHP 手冊: 字串操作](http://php.net/manual/en/language.operators.string.php) 111 | * [PHP 手冊: 字串函數](http://php.net/manual/en/ref.strings.php) 112 | * [`strpos()`](http://php.net/manual/en/function.strpos.php) 113 | * [`strlen()`](http://php.net/manual/en/function.strlen.php) 114 | * [`substr()`](http://php.net/manual/en/function.substr.php) 115 | * [PHP 手冊: 多位元字串函數](http://php.net/manual/en/ref.mbstring.php) 116 | * [`mb_strpos()`](http://php.net/manual/en/function.mb-strpos.php) 117 | * [`mb_strlen()`](http://php.net/manual/en/function.mb-strlen.php) 118 | * [`mb_substr()`](http://php.net/manual/en/function.mb-substr.php) 119 | * [`mb_internal_encoding()`](http://php.net/manual/en/function.mb-internal-encoding.php) 120 | * [`mb_http_output()`](http://php.net/manual/en/function.mb-http-output.php) 121 | * [`htmlentities()`](http://php.net/manual/en/function.htmlentities.php) 122 | * [`htmlspecialchars()`](http://www.php.net/manual/en/function.htmlspecialchars.php) 123 | * [PHP UTF-8 一覽表](http://blog.loftdigital.com/blog/php-utf-8-cheatsheet) 124 | * [Stack Overflow: 是什麼原因讓 PHP 與 Unicode 不相容?](http://stackoverflow.com/questions/571694/what-factors-make-php-unicode-incompatible) 125 | * [Stack Overflow: PHP 和 MySQL 處理各種語言字串的最佳實務](http://stackoverflow.com/questions/140728/best-practices-in-php-and-mysql-with-international-strings) 126 | * [如何使 MySQL 資料庫完整支援 Unicode](http://mathiasbynens.be/notes/mysql-utf8mb4) 127 | * [使用可攜式 UTF-8 方式來使 PHP 支援 Unicode](http://www.sitepoint.com/bringing-unicode-to-php-with-portable-utf8/) 128 | -------------------------------------------------------------------------------- /_posts/06-01-01-Dependency-Injection.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 依賴注入 3 | anchor: dependency_injection 4 | --- 5 | 6 | # 依賴注入 {#dependency_injection_title} 7 | 8 | 出自 [維基百科](http://en.wikipedia.org/wiki/Dependency_injection): 9 | 10 | > 依賴注入是一個讓我們可以移除寫死的相依關係, 11 | > 並在執行期間或編譯期間改變相依關係的軟體設計模式 12 | 13 | 這句話讓這個概念聽起來比實際上來的要複雜許多。 依賴注入是透過建構式注入、方法呼叫或設定屬性的其中任何一個來提供元件跟它的相依關係。 就是這麼簡單。 14 | -------------------------------------------------------------------------------- /_posts/06-02-01-Basic-Concept.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 基本概念 4 | anchor: basic_concept 5 | --- 6 | 7 | ## 基本概念 {#basic_concept_title} 8 | 9 | 我們可以用一個簡單而樸實的例子來說明這個概念。 10 | 11 | 這裡我們有一個 `Database` 類別它需要個配接器來跟資料庫溝通。 12 | 我們在建構式實例化配接器並建立寫死的相依關係。 13 | 這使得測試困難並意味 `Database` 類別跟配接器有非常緊密地耦合。 14 | 15 | {% highlight php %} 16 | adapter = new MySqlAdapter; 26 | } 27 | } 28 | 29 | class MysqlAdapter {} 30 | {% endhighlight %} 31 | 32 | 這段程式碼可以用依賴注入重構並因此鬆開了相依關係。 33 | 34 | {% highlight php %} 35 | adapter = $adapter; 45 | } 46 | } 47 | 48 | class MysqlAdapter {} 49 | {% endhighlight %} 50 | 51 | 現在我們給 `Database` 類別它的相依關係而不是它自己建立相依關係。 我們可以建立 52 | 接受一個相依關係參數的方法並用這個方法設定它,或者 `$adapter` 屬性是 `public` 我們可以直接設定它。 53 | -------------------------------------------------------------------------------- /_posts/06-03-01-Complex-Problem.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 複雜的問題 4 | anchor: complex_problem 5 | --- 6 | 7 | ## 複雜的問題 {#complex_problem_title} 8 | 9 | 如果你曾經讀過依賴注入,那你可能已經看過 *"控制反轉"* 或 *"依賴反轉原則"* 這詞彙。 10 | 這些都是依賴注入解決的複雜問題。 11 | 12 | ### 控制反轉 13 | 14 | 控制反轉就像字面上說的,藉由維持組織的控制從我們的物件完全地分離來「反向控制」一個系統。 15 | 從依賴注入的觀點看,這意味藉由在系統別處控制和實例化它們,鬆開相依關係。 16 | 17 | 多年來,PHP 框架已經實現了控制反轉,然而,問題變成,你要反向控制哪個部分、在哪控制? 舉個例子,MVC 框架通常會提供一個超級物件或基底控制器,其他控制器必須繼承它以取用它的相依關係。 這 **就是** 控制反轉,然而,這個方法簡單地搬移它們,而不是鬆開相依關係。 18 | 19 | 依賴注入讓我們藉由在我們需要的時候只注入我們需要的相依關係,更優雅地解決這個問題, 20 | 完全不需要任何寫死的相依關係。 21 | 22 | ### 依賴反轉原則 23 | 24 | 依賴反轉原則是物件導向設計原則的 S.O.L.I.D 集合中的「D」,它陳述應該 25 | *"依賴於抽象。 而不要依賴於實例"*。 簡單的說,這意味著我們的相依關係應該是介面/契約或抽象類別而不是具體的實作。 我們可以簡單地重構上面的例子來遵循這個原則。 26 | 27 | {% highlight php %} 28 | adapter = $adapter; 38 | } 39 | } 40 | 41 | interface AdapterInterface {} 42 | 43 | class MysqlAdapter implements AdapterInterface {} 44 | {% endhighlight %} 45 | 46 | `Database` 類別現在依賴於介面而不是實例有幾個好處。 47 | 48 | 試想,你在一個團隊中工作並且配接器是由同事動工。 在我們的第一個例子中,在可以適當地在單元測試中模擬它之前,我們必須等待同事完成配接器。 現在,相依關係是個 49 | 介面/契約,因為我們知道同事將會基於那個契約來做配接器,我們可以快樂地模擬那個介面。 50 | 51 | 這個方法有一個更大的好處是我們的程式碼現在變成更可擴展的。 如果一年下來我們終究決定想要遷移到一個不同種類的資料庫,我們可以寫一個實作原本介面的配接器並注入它來取代, 52 | 當我們可以確保配接器藉由介面遵守契約規定,就不再需要一直重構了。 53 | -------------------------------------------------------------------------------- /_posts/06-04-01-Containers.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 容器 4 | anchor: containers 5 | --- 6 | 7 | ## 容器 {#containers_title} 8 | 9 | 第一件你應該了解有關依賴注入容器的事就是它跟依賴注入是不同的東西。 容器是個便利的工具能幫助我們實作依賴注入,然而,它們經常被濫用來實作一個反模式:服務定位。 注入一個依賴注入容器作為服務定位器到你的類別中,可以說是建立了比你替換的相依關係更死的相依關係在容器中。 這也讓你的程式碼更不透明並最後更難測試。 10 | 11 | 大部份現代框架都有它們自己的依賴注入容器,讓你透過設定去繫結你的相依關係。 12 | 這意味著實際上你可以寫乾淨且去耦合的程式碼,如同應用程式使用的框架。 13 | -------------------------------------------------------------------------------- /_posts/06-05-01-Further-Reading.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 延伸閱讀 4 | anchor: further_reading 5 | --- 6 | 7 | ## 延伸閱讀 {#further_reading_title} 8 | 9 | - [學習依賴注入和 PHP](http://ralphschindler.com/2011/05/18/learning-about-dependency-injection-and-php) 10 | - [什麼是依賴注入?](http://fabien.potencier.org/article/11/what-is-dependency-injection) 11 | - [依賴注入: 打個比方](http://mwop.net/blog/260-Dependency-Injection-An-analogy.html) 12 | - [依賴注入: 啊?](http://net.tutsplus.com/tutorials/php/dependency-injection-huh/) 13 | - [依賴注入作為測試工具](http://www.happyaccidents.me/dependency-injection-as-a-tool-for-testing/) 14 | -------------------------------------------------------------------------------- /_posts/07-01-01-Databases.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 資料庫 3 | anchor: databases 4 | --- 5 | 6 | # 資料庫 {#databases_title} 7 | 8 | 你的PHP程式碼常常需要使用資料庫來保存資訊,這時,你有一些選擇來連接並與你的資料庫互動,在**PHP 5.1.0之前**的推薦方案是使用一些原生的驅動程式,例如[mysqli], [pgsql], [mssql]等等。 9 | 10 | 如果你在應用程式中只使用 _一個_ 資料庫,原生的驅動程式(native driver)是一個很好的選擇。但是,假如你使用了MySQL和一點點的MSSQL,或是你需要連接到Oracle資料庫,那麼你就不能只使用同一個驅動程式,你將需要針對每個不同的資料庫學習API — 而這些行為非常的愚蠢。 11 | 12 | ## MySQL 擴充套件 13 | 14 | PHP的[mysql]擴充套件已經不再主動的開發,[官方也在PHP 5.5.0時不贊成使用],意思是mysql擴充套件將在接下來的幾個釋出版本移除。如果你正在使用類似`mysql_connect()` 和 `mysql_query()`這些`mysql_*`開頭的函式,這些函式將在未來的幾個版本不能使用了。這代表你將需要重寫程式,所以最好的選擇就是開始使用 [mysqli] 或是 [PDO]。 15 | 16 | **如果你正要開始寫程式,那麼絕對不要使用 [mysql] 擴充套件: 請轉而使用[MySQLi extension][mysqli]或是使用[PDO]。** 17 | 18 | * [PHP: 為MySQL選擇API](http://php.net/manual/en/mysqlinfo.api.choosing.php) 19 | * [MySQL開發者們的PDO教學](http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers) 20 | 21 | ## PDO 擴充套件 22 | 23 | [PDO] 是從 — PHP 5.1.0 — 可以開始使用的一個和資料庫連結的抽象程式庫,它提供了一個共同的介面來和不同的資料庫溝通。例如,你可以使用同一套程式碼和MySQL或是SQLite溝通。 24 | 25 | {% highlight php %} 26 | // PDO + MySQL 27 | $pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password'); 28 | $statement = $pdo->query("SELECT some\_field FROM some\_table"); 29 | $row = $statement->fetch(PDO::FETCH_ASSOC); 30 | echo htmlentities($row['some_field']); 31 | 32 | // PDO + SQLite 33 | $pdo = new PDO('sqlite:/path/db/foo.sqlite'); 34 | $statement = $pdo->query("SELECT some\_field FROM some\_table"); 35 | $row = $statement->fetch(PDO::FETCH_ASSOC); 36 | echo htmlentities($row['some_field']); 37 | {% endhighlight %} 38 | 39 | PDO不會轉譯你的SQL請求,或是模擬失去的特性。它只是簡單的使用同一個API去連向各個不同類型的資料庫。最重要的是, `PDO` 允許你簡單的直接將外部的輸入插進你的SQL請求,而不需要擔心SQL注入攻擊(SQL injection attacks)。讓我們假設一個PHP腳本接收一個數字ID當作請求參數。這個ID是用來從資料庫取得使用者的資料。這是`錯誤`的使用方式。 40 | 41 | {% highlight php %} 42 | query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- 不! 45 | {% endhighlight %} 46 | 47 | 這是一個很糟糕的程式碼,你正在插入一個未處理過的請求參數到資料庫請求中。這會導致你在一瞬間被駭入。用一個實際的案例來解釋這個情況:[資料庫注入攻擊](http://wiki.hashphp.org/Validation)。想像一下有一個駭客使用一個網址類似這樣`http://domain.com/?id=1%3BDELETE+FROM+users`。這將會造成 `$_GET['id']` 變數的值變成 `1;DELETE FROM users` ,這會刪除所有你的使用者!因此你應該要使用PDO bound parameters對這個ID輸入消毒。 48 | 49 | {% highlight php %} 50 | prepare('SELECT name FROM users WHERE id = :id'); 53 | $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT); // <-- PDO自動消毒這個變數 54 | $stmt->execute(); 55 | {% endhighlight %} 56 | 57 | 這個是正確的程式碼,它在PDO敘述上使用了bound parameter。這些動作在被放入資料庫請求之前逃脫了外部的ID輸入以避免潛藏的資料庫注入攻擊。 58 | 59 | * [學習PDO] 60 | 61 | 你應該要注意到資料庫連線沒有自行關閉而耗盡資源的問題,這個問題不是沒有前例的。當使用PDO的時候,你可以藉由摧毀物件來自行結束連線,以確保所有關聯到這個物件的東西都被刪除。例如,將它的值設為NULL。如果你沒有明確的將它關閉,那麼PHP會在腳本結束時自動的關閉連線,除非你使用的是持續的連線。 62 | 63 | * [學習PDO連線] 64 | 65 | [學習PDO]: http://www.php.net/manual/en/book.pdo.php 66 | [學習PDO連線]: http://php.net/manual/en/pdo.connections.php 67 | [官方也在PHP 5.5.0時不贊成使用]: http://php.net/manual/en/migration55.deprecated.php 68 | [資料庫注入攻擊]: http://wiki.hashphp.org/Validation 69 | 70 | [pdo]: http://php.net/pdo 71 | [mysql]: http://php.net/mysql 72 | [mysqli]: http://php.net/mysqli 73 | [pgsql]: http://php.net/pgsql 74 | [mssql]: http://php.net/mssql 75 | -------------------------------------------------------------------------------- /_posts/07-02-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 | {% highlight php %} 12 |
    13 | query('SELECT * FROM table') as $row) { 15 | echo "
  • ".$row['field1']." - ".$row['field1']."
  • "; 16 | } 17 |
18 | {% endhighlight %} 19 | 20 | 這是一個很糟的例子,主要是因為它很難除蟲,很難測試,很難閱讀,而且沒有限制的話,它將會輸出過多資料。 21 | 22 | 而有非常多的方法可以達到同樣的目的,一切都看你喜歡哪種,[物件導向](/#object-oriented-programming) 或是 [函數編程](/#functional-programming) - 但是一定要有基本的分離。 23 | 24 | 考慮以下最基本的步驟: 25 | 26 | {% highlight php %} 27 | query('SELECT * FROM table'); 30 | } 31 | 32 | foreach (getAllFoos($db) as $row) { 33 | echo "
  • ".$row['field1']." - ".$row['field1']."
  • "; // 不好!! 34 | } 35 | {% endhighlight %} 36 | 37 | 這是一個好的開始,將這兩個程式碼放在不同的兩個檔案,你將會得到一些乾淨的分離。 38 | 39 | 創造一個類別來放置這個方法,你將會得到一個 "Model",再創造一個簡單的 `.php` 檔案來放入表達的邏輯,你將會得到一個 "View",這非常接近 [MVC] - 一個對多數的 [frameworks](/#frameworks_title) 非常普遍的物件導向架構。 40 | 41 | **foo.php** 42 | 43 | {% highlight php %} 44 | db = $db; 70 | } 71 | 72 | public function getAllFoos() { 73 | return $this->db->query('SELECT * FROM table'); 74 | } 75 | } 76 | {% endhighlight %} 77 | 78 | **views/foo-list.php** 79 | 80 | {% highlight php %} 81 | 82 | - 83 | 84 | {% endhighlight %} 85 | 86 | 這是對許多現代的frameworks非常基本的架構,你不需要每次都做這些,但是將表達邏輯和資料庫溝通的部分混合,在想要做[單元測試](/#unit-testing)的情況下,這是一個非常真實的問題 87 | 88 | [PHPBridge] 有個叫 [Creating a Data Class] 的資源來說明非常相似的主題,而且對剛開始接觸這些概念的程式開發者而言,這是一個不錯的開始。 89 | 90 | [MVC]: http://code.tutsplus.com/tutorials/mvc-for-noobs--net-10488 91 | [PHPBridge]: http://phpbridge.org/ 92 | [Creating a Data Class]: http://phpbridge.org/intro-to-php/creating_a_data_class -------------------------------------------------------------------------------- /_posts/07-03-01-Abstraction-Layers.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 抽象層 4 | anchor: databases_abstraction_layers 5 | --- 6 | 7 | ## 抽象層 {#databases_abstraction_layers_title} 8 | 9 | 許多的framework提供他們自己的抽象層,有些會建立在PDO的上層,這些抽象層常常會藉由將你的資料庫請求包裝起來,以模擬一些非你正在使用的資料庫所擁有的特色,提供你一個真正的資料庫抽象,而不是僅僅是PDO所提供的連線抽象。這些抽象層當然地會造成一些小小的效能負擔,但是你如果你正在建立一個應用程式需要與MySQL, PostgreSQL和SQLite的時候,這些代價來交換程式碼的乾淨是值得的。 10 | 11 | 有些抽象層是建立在 [PSR-0][psr0] 或 [PSR-4][psr4] 的命名空間標準,所以可以被安裝在任何你喜歡的應用程式裡。 12 | 13 | * [Aura SQL][6] 14 | * [Doctrine2 DBAL][2] 15 | * [Propel][7] 16 | * [ZF2 Db][4] 17 | 18 | [1]: http://www.php.net/manual/en/book.pdo.php 19 | [2]: http://www.doctrine-project.org/projects/dbal.html 20 | [4]: http://packages.zendframework.com/docs/latest/manual/en/index.html#zend-db 21 | [6]: https://github.com/auraphp/Aura.Sql 22 | [7]: http://propelorm.org/ 23 | 24 | [psr0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md 25 | [psr4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md 26 | -------------------------------------------------------------------------------- /_posts/08-01-01-Templating.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用樣板 3 | anchor: templating 4 | --- 5 | 6 | # 使用樣板 {#templating_title} 7 | 樣板提供簡便的方式,將展現邏輯從控制器與業務邏輯中分離出來。 8 | 9 | 一般而言,樣板包含了應用程式的HTML,但也可以套用在其他的格式上,例如XML。 10 | 11 | 樣板通常也被當作「視圖」, 而它是 [模型-視圖-控制器](pages/Design-Patterns.html#model-view-controller) (MVC) 軟體架構模式第二個元素的**一部份**。 12 | -------------------------------------------------------------------------------- /_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 | 樣板同時也改善了前端程式碼的組織架構。一般而言,樣板放置在「視圖」的資料夾中,每個樣板定義在各自獨立的檔案中。這種方式鼓勵程式碼重用,它將大區塊的程式碼分為較小、可重用的段落,通常稱作局部樣板 (partials) 。舉例來說,網站的頭、尾區塊可以各自定義為樣板,之後將它們置於每一個頁面樣板的上、下位置。 12 | 13 | 最後,端視你所使用的函式庫,樣板可以透過自動逸脫使用者產出內容,帶來更多的安全性。有些函式庫甚至提供沙箱機制,樣板設計師只能使用在白名單中的變數和函式。 14 | -------------------------------------------------------------------------------- /_posts/08-03-01-Plain-PHP-Templates.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 原生 PHP 樣板 4 | anchor: plain_php_templates 5 | --- 6 | 7 | ## 原生 PHP 樣板 {#plain_php_templates_title} 8 | 9 | 原生 PHP 樣板是直接拿 PHP 來寫樣板。這是很直覺的選擇,因為 PHP 事實上就是個樣板語言。這代表你可以在其他的語言中結合 PHP 使用,像是 HTML。這對 PHP 開發者相當有利,不需要額外學習新的語法,他們熟知可以使用的函式,所使用的編輯器也已經內建語法高亮和自動補完。此外,原生 PHP 樣板少了編譯階段,速度更快。 10 | 11 | 現今的 PHP 框架都會使用一些樣板系統,這當中多數預設使用原生的 PHP 語法。在框架之外,一些函式庫如 [Plates](http://platesphp.com/) 或 [Aura.View](https://github.com/auraphp/Aura.View) ,提供現今樣板常見功能,像是繼承、佈局、擴充套件等,讓原生PHP樣板更容易使用。 12 | 13 | ### 原生 PHP 樣板的範例 14 | 15 | 使用 [Plates](http://platesphp.com/) 函式。 16 | 17 | {% highlight php %} 18 | 19 | 20 | insert('header', ['title' => 'User Profile']) ?> 21 | 22 |

    User Profile

    23 |

    Hello, escape($name)?>

    24 | 25 | insert('footer') ?> 26 | {% endhighlight %} 27 | 28 | ### 原生 PHP 樣板使用繼承的範例 29 | 30 | 使用 [Plates](http://platesphp.com/) 函式。 31 | 32 | {% highlight php %} 33 | 34 | 35 | 36 | 37 | <?=$title?> 38 | 39 | 40 | 41 |
    42 | section('content')?> 43 |
    44 | 45 | 46 | 47 | {% endhighlight %} 48 | 49 | {% highlight php %} 50 | 51 | 52 | layout('template', ['title' => 'User Profile']) ?> 53 | 54 |

    User Profile

    55 |

    Hello, escape($name)?>

    56 | {% endhighlight %} 57 | -------------------------------------------------------------------------------- /_posts/08-04-01-Compiled-Templates.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 編譯樣板 4 | anchor: compiled_templates 5 | --- 6 | 7 | ## 編譯樣板 {#compiled_templates} 8 | 9 | PHP 不斷進化為成熟、物件導向的語言,但它作為樣板語言[沒有改善多少](http://fabien.potencier.org/article/34/templating-engines-in-php)。編譯樣板,像是 [Twig](http://twig.sensiolabs.org/) 或 [Smarty](http://www.smarty.net/)*,提供樣板專用的新語法,補足了這片空白。從自動逸脫到繼承以及簡化控制結構,編譯樣板設計地更容易撰寫、可讀性更高、使用上更為安全。編譯樣板更能在不同的語言中使用,[Mustache](http://mustache.github.io/) 就是一個極佳例子。由於這些樣板需要編譯,在效能方面會帶來些許影響,不過如果適當使用快取,影響就十分有限。 10 | 11 | **雖然 Smarty 提供自動逸脫機制,不過這個功能並非預設啟用。* 12 | 13 | ### 編譯樣板範例 14 | 15 | 使用 [Twig](http://twig.sensiolabs.org/) 函式庫。 16 | 17 | {% highlight text %} 18 | {% raw %} 19 | {% include 'header.html' with {'title': 'User Profile'} %} 20 | 21 |

    User Profile

    22 |

    Hello, {{ name }}

    23 | 24 | {% include 'footer.html' %} 25 | {% endraw %} 26 | {% endhighlight %} 27 | 28 | ### 編譯模例使用繼承的範例 29 | 30 | 使用 [Twig](http://twig.sensiolabs.org/) 函式庫。 31 | 32 | {% highlight text %} 33 | {% raw %} 34 | // template.php 35 | 36 | 37 | 38 | {% block title %}{% endblock %} 39 | 40 | 41 | 42 |
    43 | {% block content %}{% endblock %} 44 |
    45 | 46 | 47 | 48 | {% endraw %} 49 | {% endhighlight %} 50 | 51 | {% highlight text %} 52 | {% raw %} 53 | // user_profile.php 54 | 55 | {% extends "template.html" %} 56 | 57 | {% block title %}User Profile{% endblock %} 58 | {% block content %} 59 |

    User Profile

    60 |

    Hello, {{ name }}

    61 | {% endblock %} 62 | {% endraw %} 63 | {% endhighlight %} 64 | -------------------------------------------------------------------------------- /_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/article/34/templating-engines-in-php) 12 | - [An Introduction to Views & Templating in CodeIgniter](http://code.tutsplus.com/tutorials/an-introduction-to-views-templating-in-codeigniter--net-25648) 13 | - [Getting Started With PHP Templating](http://www.smashingmagazine.com/2011/10/17/getting-started-with-php-templating/) 14 | - [Roll Your Own Templating System in PHP](http://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](http://code.tutsplus.com/tutorials/working-with-templates-in-symfony-2--cms-21172) 17 | 18 | ### 函式庫 19 | 20 | - [Aura.View](https://github.com/auraphp/Aura.View) *(原生)* 21 | - [Blade](http://laravel.com/docs/templates) *(編譯, 框架限定)* 22 | - [Dwoo](http://dwoo.org/) *(編譯)* 23 | - [Latte](https://github.com/nette/latte) *(編譯)* 24 | - [Mustache](https://github.com/bobthecow/mustache.php) *(編譯)* 25 | - [PHPTAL](http://phptal.org/) *(編譯)* 26 | - [Plates](http://platesphp.com/) *(原生)* 27 | - [Smarty](http://www.smarty.net/) *(編譯)* 28 | - [Twig](http://twig.sensiolabs.org/) *(編譯)* 29 | - [Zend\View](http://framework.zend.com/manual/2.3/en/modules/zend.view.quick-start.html) *(原生, 框架限定)* 30 | -------------------------------------------------------------------------------- /_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 | isChild: true 3 | title: 錯誤 4 | anchor: errors 5 | --- 6 | 7 | ## 錯誤 {#errors_title} 8 | 9 | 在許多「重例外」(exception-heavy) 程式語言中,一旦發生錯誤,就會丟出例外。這的確是可行的運作方式,不過 PHP 卻是「輕例外」(exception-light) 語言。當然它具備例外,在和物件作用時,核心也開始採用這個機制來處理,只是PHP 會盡其可能保持運作,無視發生的事情,除非是嚴重錯誤。 10 | 11 | 舉例而言: 12 | 13 | {% highlight php %} 14 | $ php -a 15 | php > echo $foo; 16 | Notice: Undefined variable: foo in php shell code on line 1 17 | {% endhighlight %} 18 | 19 | 這裡只是 notice 層級的錯誤, PHP 仍然會愉快地繼續執行。這對來自「重例外」語言的人可能會帶來困擾,例如在 Python 中,存取一個不存在的變數會丟出例外: 20 | 21 | {% highlight python %} 22 | $ python 23 | >>> print foo 24 | Traceback (most recent call last): 25 | File "", line 1, in 26 | NameError: name 'foo' is not defined 27 | {% endhighlight %} 28 | 29 | 本質上的差異在於任何小事都會驚嚇到 Python,因此開發人員可以確信任何潛在的問題或邊緣案例都能捕捉到,相同的情況 PHP 仍然會保持運作,除非極端的問題發生,這時才會丟出錯誤並回報。 30 | 31 | ### 錯誤嚴重性 32 | 33 | PHP 有幾個錯誤嚴重性等級。三個最常見的訊息類型是錯誤(error)、通知(notice)和警告(warning)。它們有不同的嚴重性:「E_ERROR」、「E_NOTICE」和「E_WARNING」。錯誤是執行期的嚴重問題,通常是因為程式碼出錯而造成,而且必須修正它,因為它會使 PHP 停止運作。警告是非嚴重性的錯誤,程式執行不會因此而中止。通知是建議性質的訊息,是因為程式碼在執行期有潛在機會造成問題,程式不會因此停止。 34 | 35 | 另一個在編譯時期回報錯誤的訊息類型是「E_STRICT」。這個訊息用來建議修改程式碼,才能維持最佳的運作性並能與日後的 PHP 版本相容。 36 | 37 | ### 改變 PHP 的錯誤回報行為 38 | 39 | 錯誤回報可以藉由 PHP 設定及/或函式呼叫變更。使用內建的 PHP 函式 「error_reporting()」,可以設定程式執行期間的錯誤等級,方法是傳遞預先定義的錯誤等級常數,意謂如果你只想看到警告和錯誤(而非通知),那你可以這樣設定: 40 | 41 | {% highlight php %} 42 | error_reporting(E_ERROR | E_WARNING); 43 | {% endhighlight %} 44 | 45 | 你也可以決定錯誤是否在螢幕上顯示(開發時好用)或隱藏後記錄下來(適用於正式環境)。想知道更多細節,可以查看 [錯誤報告][errorreport] 章節。 46 | 47 | ### 行內錯誤抑制 48 | 49 | 你可以讓 PHP 利用錯誤控制運算子「@」來抑制特定的錯誤。將這個運算子放置在表達式之前,其後的任何錯誤都不會出現。 50 | 51 | {% highlight php %} 52 | echo @$foo['bar']; 53 | {% endhighlight %} 54 | 55 | 如果「$foo['bar']」存在,程式會將結果輸出,不過如果變數「$foo」或「bar」鍵值不存在,將會回傳空值或不輸出任何東西。假如不使用錯誤控制運算子,這個運算式會建立一個錯誤訊息「PHP Notice: Undefined variable: foo」或「PHP Notice: Undefined index: bar」。 56 | 57 | 這看起來像是個好主意,不過卻也有些討厭的代價。PHP 處理使用「@」的運算式,比起不用時會犧牲一些效能。過早最佳化在所有程式中也許都是爭論點,不過效能在你的應用程式/函式中佔有重要地位,那麼了解錯誤控制運算子的效能影響就很重要。 58 | 59 | 其次,錯誤控制運算子 **完全** 吃掉錯誤。不但沒有顯示,而且也不會記錄在錯誤記錄中。此外,在正式環境 PHP 沒有辦法關閉錯誤控制運算子。也許你沒錯,那些錯誤是無害的,不過有些較具傷害性的錯誤同時也被隱藏。 60 | 61 | 如果有方法可以避免錯誤抑制運算子,你應該考慮使用,舉例來說,上面的程式碼可以這樣重寫: 62 | 63 | {% highlight php %} 64 | echo isset($foo['bar']) ? $foo['bar'] : ''; 65 | {% endhighlight %} 66 | 67 | 「fopen()」載入檔案失敗時,也許是一個使用錯誤抑制的合理例子。你可以在嘗試載入檔案前檢查是否存在,但是這個檔案如果在檢查後才被刪除,而「fopen()」還沒執行(聽起來有點不太可能,但就是會發生),這時「fopen()」將會回傳false _並且_ 丟出錯誤。這也許該由 PHP 本身來解決,但這就是一個錯誤抑制才能有效解決的例子。 68 | 69 | 前面我們提到在正式 PHP 環境沒有辦法關閉錯誤控制運算子。但是 [xDebug] 有「xdebug.scream」的 ini 設定,可以關閉錯誤控制運算子。你可以依照下面的方式修改「php.ini」: 70 | 71 | xdebug.scream = On 72 | 73 | 你也可以在執行期間透過「ini_set」函式設定這個值: 74 | 75 | {% highlight php %} 76 | ini_set('xdebug.scream', '1') 77 | {% endhighlight %} 78 | 79 | 「[Scream]」這個 PHP 擴充套件提供和 xDebug 類似的功能,只是 Scream 的 ini 設定叫做「scream.enabled」。 80 | 81 | 當你在除錯而懷疑錯誤資訊被隱藏時,這是最有用的方法。Scream 務必小心使用,而且只拿來當作暫時性的除錯工具。有許多 PHP 函式庫也許無法在錯誤運算子停用時正常運作。 82 | 83 | 84 | * [Error Control Operators](http://php.net/manual/en/language.operators.errorcontrol.php) 85 | * [SitePoint](http://www.sitepoint.com/) 86 | * [xDebug] 87 | * [Scream] 88 | 89 | [xDebug]: http://xdebug.org/docs/basic 90 | [Scream]: http://www.php.net/manual/en/book.scream.php 91 | 92 | ### ErrorException 類別 93 | 94 | PHP 可以完美化身為「重例外」程式語言,只需要幾行程式碼就能切換過去。基本上你可以利用「ErrorException」類別丟出「錯誤」來當作「例外」,這個類別是擴充「Exception」類別而來。 95 | 96 | 這在許多現今框架中是常見的實務作法,像是 Symfony 和 Laravel 都是。Laravel 預設會透過 [Whoops!] 套件,將錯誤當作例外顯示出來,如果「app.debug」啟用的話;關閉則會隱藏。 97 | 98 | 丟出錯誤作為例外,在開發過程中可以更好地處理它,如果在開發時看到例外,你可以將它包在 catch 敘述中,再寫下如何處理的程式。捕捉每一個例外,都會使應用程式越來越穩固。 99 | 100 | 更多關於如何使用「ErrorException」來處理錯誤的細節,可以參考 [ErrorException Class][errorexception]。 101 | 102 | * [Error Control Operators](http://php.net/manual/en/language.operators.errorcontrol.php) 103 | * [Predefined Constants for Error Handling](http://www.php.net/manual/en/errorfunc.constants.php) 104 | * [error_reporting](http://www.php.net/manual/en/function.error-reporting.php) 105 | * [Reporting][errorreport] 106 | 107 | [errorexception]: http://php.net/manual/en/class.errorexception.php 108 | [errorreport]: /#error_reporting 109 | [Whoops!]: http://filp.github.io/whoops/ 110 | -------------------------------------------------------------------------------- /_posts/09-03-01-Exceptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 例外 4 | anchor: exceptions 5 | --- 6 | 7 | ## 例外 {#exceptions_title} 8 | 9 | 例外是許多流行語言的標準配備,但它們往往被 PHP 開發人員忽視。像 Ruby 就是一個極端重視例外的語言,無論有什麼不對勁發生,像是 HTTP 請求失敗,或是資料庫的查詢有問題,甚至像找不到圖片,Ruby (或是所使用的gem),將會丟出例外到畫面上,讓你立刻了解問題發生了。 10 | 11 | PHP 看待這事則是相當隨意,呼叫「file_get_contents()」通常只會給出「FALSE」值和警告。許多較早的 PHP 框架像是 CodeIgniter 只是回傳 false、將訊息寫入記錄,頂多讓你使用像「$this->upload->get_error()」來檢視錯誤原因。這裡的問題出在你必須找出錯誤所在,並且檢視文件來查看這個類別使用什麼樣的錯誤方法,而不是明顯曝露錯誤。 12 | 13 | 另一個問題是發生在類別自動丟出錯誤到畫面並跳出程序。當你這樣做時,其他開發者動態處理錯誤的機會也被擋下了。例外應該丟出能讓開發人員意識到錯誤的存在,讓他們可以選擇處理的方式,例如: 14 | 15 | {% highlight php %} 16 | subject('My Subject'); 19 | $email->body('How the heck are you?'); 20 | $email->to('guy@example.com', 'Some Guy'); 21 | 22 | try 23 | { 24 | $email->send(); 25 | } 26 | catch(Fuel\Email\ValidationFailedException $e) 27 | { 28 | // 驗證失敗 29 | } 30 | catch(Fuel\Email\SendingFailedException $e) 31 | { 32 | // 無法寄出信件 33 | } 34 | finally 35 | { 36 | // 無論丟出什麼樣的例外都會執行,並且在正常程序繼續之前執行 37 | } 38 | {% endhighlight %} 39 | 40 | ### SPL 例外 41 | 42 | 原生的「例外」類別沒有提供太多除錯脈絡給開發人員,要修正的解法是建立一個特殊的「Exception」類型,方式是從原生「Exception」類別建立一個子類別: 43 | 44 | {% highlight php %} 45 | 許多客製例外,其中有些已經避免使用在 [SPL 擴充套件][splext] 提供的 SPL 例外。 50 | 51 | 舉例來說,如果你使用「__call()」魔術方法去呼叫了無效的方法,而不是丟出模糊的標準 Exception,或是建立客製例外專門處理,你可能已經幹了「throw new BadMethodCallException;」。 52 | 53 | * [Read about Exceptions][exceptions] 54 | * [Read about SPL Exceptions][splexe] 55 | * [Nesting Exceptions In PHP][nesting-exceptions-in-php] 56 | * [Exception Best Practices in PHP 5.3][exception-best-practices53] 57 | 58 | [exceptions]: http://php.net/manual/en/language.exceptions.php 59 | [splexe]: http://php.net/manual/en/spl.exceptions.php 60 | [splext]: /#standard_php_library 61 | [exception-best-practices53]: http://ralphschindler.com/2010/09/15/exception-best-practices-in-php-5-3 62 | [nesting-exceptions-in-php]: http://www.brandonsavage.net/exceptional-php-nesting-exceptions-in-php/ 63 | -------------------------------------------------------------------------------- /_posts/10-01-01-Security.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 安全 3 | anchor: security 4 | --- 5 | 6 | # 安全 {#security_title} 7 | -------------------------------------------------------------------------------- /_posts/10-02-01-Web-Application-Security.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: Web 應用程式安全 4 | anchor: web_application_security 5 | --- 6 | 7 | ## Web 應用程式安全 {#web_application_security_title} 8 | 9 | 總是有些不懷好意的人想鑽你 Web 應用程式的漏洞,所以採取一些必要的防範措施來強化 Web 應用程式的安全性是相當重要的。很幸運的, [The Open Web Application Security Project][1] (OWASP) 已經整理一份相當詳細的資安列表以及防範的對策。如果你是一個有資安意識的開發者,那麼就必須詳細的閱讀它。 10 | 11 | * [查閱 OWASP 安全指南][2] 12 | 13 | [1]: https://www.owasp.org/ 14 | [2]: https://www.owasp.org/index.php/Guide_Table_of_Contents 15 | -------------------------------------------------------------------------------- /_posts/10-03-01-Password-Hashing.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 密碼雜湊 4 | anchor: password_hashing 5 | --- 6 | 7 | ## 密碼雜湊 {#password_hashing_title} 8 | 9 | 每個人在建構 PHP 應用程式時終究會加入使用者登入的功能。使用者的帳號及密碼都會被儲存在資料庫,並在登入時使用該資料來驗證使用者。 10 | 11 | 在寫入資料前正確的 [_雜湊_][3] 他們的密碼是相當重要的一件事。使用單向雜湊函式處理密碼可以產生不可逆的密碼雜湊,而該雜湊會是一段固定長度的字串且無法逆向演算出密碼。這就代表你可以雜湊另一組密碼,並比較兩者是否來自於同一組密碼,而且你無法得知原始的密碼。如果你不將密碼雜湊,那麼當未經授權的第三者進入你的資料庫時,所有使用者的帳號資料將會一覽無遺。有些使用者可能(很不幸的)在別的服務也使用相同的密碼,所以務必要重視資訊安全的問題。 12 | 13 | **使用 `password_hash` 來雜湊密碼** 14 | 15 | `password_hash` 已經在 PHP 5.5 時加入。現在你可以使用目前 PHP 所支援的演算法中最強大的 BCrypt 。當然,未來支援更多的演算法時他就不見得會是最強的。 `password_compat` 函式庫的出現是為了提供 PHP >= 5.3.7 對舊版本的支援性。 16 | 17 | 在下面例子中,我們雜湊一組字串,然後和新的雜湊值做比對。因為我們使用的兩組字串是不同的( 'secret-password' 與 'bad-password' ),所以理所當然的就登入失敗了。 18 | 19 | {% highlight php %} 20 | = 5.3.7 && < 5.5 的 `password_compat`] [2] 37 | * [了解密碼學中的雜湊] [3] 38 | * [PHP `password_hash` RFC] [4] 39 | 40 | [1]: http://us2.php.net/manual/en/function.password-hash.php 41 | [2]: https://github.com/ircmaxell/password_compat 42 | [3]: http://en.wikipedia.org/wiki/Cryptographic_hash_function 43 | [4]: https://wiki.php.net/rfc/password_hash 44 | -------------------------------------------------------------------------------- /_posts/10-04-01-Data-Filtering.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 資料過濾 4 | anchor: data_filtering 5 | --- 6 | 7 | ## 資料過濾 {#data_filtering_title} 8 | 9 | 絕對(永遠)不要在 PHP 程式相信外部輸入。每次在程式中使用外部輸入時務必要過濾及驗證。 `filter_var` 及 `filter_input` 函式可以過濾文字及驗證文字的格式(例如 Email )。 10 | 11 | 外部輸入可以是:由表單輸入的 `$_GET` 及 `$_POST` ,還有 `$_SERVER` 超級全域變數,以及來自 `fopen('php://input', 'r')` 的 Http 請求。切記,外部輸入並不限定是使用者從表單填入的資料。上傳及下載的檔案, Session 值及 Cookie 資料,以及第三方 Web 服務提供的資料,均是外部輸入。 12 | 13 | 當外部輸入被儲存、合併,或是下次讀取,他們依然是外部輸入。每次在程式中處理、輸出、串接或是引入資料時,試著問你自己這些資料都已經過濾,而且可以被信任。 14 | 15 | 根據資料使用的方式不同,就要進行不同的_過濾_。例如當外部輸入未經過濾輸出到 HTML 頁面上時,他就可以在你的網站上執行 HTML 及 JavaScript !這就是我們所知道的跨網站指令碼( Cross-site scripting , 又稱為 XSS ),這是一個相當危險的攻擊手法。避免 XSS 的其中一個方式就是使用 `strip_tags` 函式來過濾外部輸入的所有 HTML 標籤,另外也可以使用 `htmlentities` 或 `htmlspecialchars` 函式將特定字元替換成 HTML 的實體符號。 16 | 17 | 另一個例子是傳遞給終端機執行的選項,這是相當危險的一件事(而且通常是個爛主意),不過你可以使用內建的 `escapeshellarg` 函式來過濾執行終端機的參數。 18 | 19 | 最後一個例子就是根據外部輸入從檔案系統載入檔案,該動作可以透過修檔案名稱或是路徑來攻擊。你必須移除外部輸入的 "/" , "../" , [空字元][6] 或是其他檔案路徑的字元,這樣就可以避免載入隱密,非公開或是敏感的檔案。 20 | 21 | * [了解資料過濾][1] 22 | * [了解 `filter_var`][4] 23 | * [了解 `filter_input`][5] 24 | * [了解空字元的處理][6] 25 | 26 | ### 淨化 27 | 28 | 淨化就是刪除(或是跳脫)外部輸入不合法或是不安全的字元。 29 | 30 | 例如,在將外部輸入的字元輸出至 HTML 或是插入到SQL的查詢前淨化外部輸入。當你使用 [PDO](#databases) 綁定參數時,他會為你淨化輸入的資料。 31 | 32 | 有時需要允許外部輸入包含某些安全的 HTML 標籤,並輸出至HTML頁面上。這是很難做到的,可以試著使用其他限制較嚴格的格式,例如 Markdown 或 BBCode 。如果真的窮途末路了,可以使用 [HTML Purifier][html-purifier] 來進行淨化。 33 | 34 | [查閱淨化過濾][2] 35 | 36 | ### 驗證 37 | 38 | 驗證可以確保外部輸入是你所期望的資料。例如,你可能需要在處理註冊帳號時驗證Email,電話號碼或年齡。 39 | 40 | [查閱驗證過濾][3] 41 | 42 | [1]: http://www.php.net/manual/en/book.filter.php 43 | [2]: http://www.php.net/manual/en/filter.filters.sanitize.php 44 | [3]: http://www.php.net/manual/en/filter.filters.validate.php 45 | [4]: http://php.net/manual/en/function.filter-var.php 46 | [5]: http://www.php.net/manual/en/function.filter-input.php 47 | [6]: http://php.net/manual/en/security.filesystem.nullbytes.php 48 | [html-purifier]: http://htmlpurifier.org/ 49 | -------------------------------------------------------------------------------- /_posts/10-05-01-Configuration-Files.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 設定檔 4 | anchor: configuration_files 5 | --- 6 | 7 | ## 設定檔 {#configuration_files_title} 8 | 9 | 當你的應用程式建立設定檔時,建議遵照以下最佳實踐: 10 | 11 | - 設定檔應該儲存在 Web 不能直接存取及上傳的檔案目錄中 12 | - 如果設定檔只能放在文件的根目錄,請使用 `.php` 作為檔案的副檔名。這樣可以確保即使檔案被直接存取,內容也不會被當作純文字輸出。 13 | - 通過加密或是調整檔案系統權限讓設定檔的內容應該得到應有的保護。 14 | -------------------------------------------------------------------------------- /_posts/10-06-01-Register-Globals.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 註冊全域變數 4 | anchor: register_globals 5 | --- 6 | 7 | ## 註冊全域變數 {#register_globals_title} 8 | 9 | **注意:** 從 PHP 5.4.0 開始, `register_globals` 的設定已經被移除且不再被支援。如果你還保留這個設定,就意味著你該更新你的應用程式了。 10 | 11 | 當開啟 `register_globals` 設定時,`$_POST` , `$_GET` 及 `$_REQUEST` 中的變數會自動註冊為全域變數,此時你的應用程式將無法辨識資料的原始來源,導致相當容易產生安全的漏洞。 12 | 13 | 例如: `$_GET['foo']` 會被註冊為全域變數 `$foo` ,他將會覆蓋掉程式中未定義的變數。 14 | 如果你的使用的 PHP < 5.4.0 ,請__再三確認__已經把 `register_globals` 給__關閉__。 15 | 16 | * [在 PHP manual 中了解註冊 _globals](http://www.php.net/manual/en/security.globals.php) 17 | -------------------------------------------------------------------------------- /_posts/10-07-01-Error-Reporting.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 錯誤報告 4 | anchor: error_reporting 5 | --- 6 | 7 | ## 錯誤報告 {#error_reporting_title} 8 | 9 | 錯誤報告可以用來找到應用程式的錯誤,但是也可能將應用程式的結構暴露在外,產生安全性的問題。為了防止這類問題的發生,你需要為你的開發環境及上線環境進行不同的設定。 10 | 11 | ### 開發環境 12 | 13 | 如果要在開發環境顯示錯誤訊息,你需要在 `php.ini` 中進行以下設定: 14 | 15 | display_errors = On 16 | display_startup_errors = On 17 | error_reporting = -1 18 | log_errors = On 19 | 20 | > 設定為 `-1` 表示顯示任何的錯誤訊息,包括未來版本新增的錯誤類型或參數,和 PHP 5.4 中的 `E_ALL` 相同。 - [php.net](http://php.net/manual/function.error-reporting.php) 21 | 22 | `E_STRICT` 錯誤參數是在 5.3.0 中加入的,並不包含在 `E_ALL` 中,但是在 5.4.0 開始,就被包含進 `E_ALL` 裡。這是什麼意思? 23 | 這就表示如果要在 PHP 5.3 中顯示所有的錯誤訊息,你必須將參數設定為 `-1` 或 `E_ALL | E_STRICT` 。 24 | 25 | **各個 PHP 版本中顯示所有錯誤的設定** 26 | 27 | * < 5.3 `-1` 或 `E_ALL` 28 | *   5.3 `-1` 或 `E_ALL | E_STRICT` 29 | * > 5.3 `-1` 或 `E_ALL` 30 | 31 | ### 線上環境 32 | 33 | 如果要在線上環境隱藏錯誤訊息,你需要在 `php.ini` 中進行以下設定: 34 | 35 | display_errors = Off 36 | display_startup_errors = Off 37 | error_reporting = E_ALL 38 | log_errors = On 39 | 40 | 在線上環境進行這個設定後,所有的錯誤訊息會紀錄至 Web 伺服器的錯誤日誌,但不會顯示至使用者畫面。欲了解更多的錯誤設定相關資訊,請參考 PHP 手冊: 41 | 42 | * [錯誤報告](http://php.net/manual/errorfunc.configuration.php#ini.error-reporting) 43 | * [顯示錯誤訊息](http://php.net/manual/errorfunc.configuration.php#ini.display-errors) 44 | * [顯示啟動錯誤訊息](http://php.net/manual/errorfunc.configuration.php#ini.display-startup-errors) 45 | * [錯誤日誌](http://php.net/manual/errorfunc.configuration.php#ini.log-errors) 46 | -------------------------------------------------------------------------------- /_posts/11-01-01-Testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 測試 3 | anchor: testing 4 | --- 5 | 6 | # 測試 {#testing_title} 7 | 8 | 為你的 PHP 程式碼撰寫自動化測試被認為是個最佳實踐,可以幫助你建立良好的 9 | 應用程式。 自動化測試是個偉大的工具,它能確保你的應用程式 10 | 在改變或增加新的功能時沒有壞掉,不應該被忽略。 11 | 12 | PHP 有一些不同種類的測試工具 (或框架) 可以使用,它們使用不同的方法 - 但他們都試圖避免手動測試和大型 QA 團隊的需求,確保最近的變更不會破壞既有的功能。 13 | -------------------------------------------------------------------------------- /_posts/11-02-01-Test-Driven-Development.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 測試驅動開發 4 | anchor: test_driven_development 5 | --- 6 | 7 | ## 測試驅動開發 {#test_driven_development_title} 8 | 9 | 出自 [維基百科](http://en.wikipedia.org/wiki/Test-driven_development): 10 | 11 | > 測試驅動開發 (TDD) 是個依賴不斷重複著非常短的開發週期來運作的軟體開發流程: 首先,開發者撰寫一個定義著期望的改善或是新函式的失敗自動測試案例,接著撰寫程式碼來使測試通過,並最終重構程式碼至可以接受的標準。 Kent Beck,被認為是開發或是 '重新發現' 這個技術的人,2003 年聲明 TDD 鼓勵了簡單的設計並提振了信心 12 | 13 | 你可以為你的應用程式做這一些不同種類的測試 14 | 15 | ### 單元測試 16 | 17 | 單元測試是個編程方法,用來確保函式、類別和方法如預期運作,從你建構它們開始持續到整個開發週期。 藉由檢查每個函式和方法的 18 | 輸入和輸出值,你可以確保內部邏輯正確地運作。 藉由使用依賴注入、建立 "mock" 類別和 stubs,你可以驗證相依關係已經正確地建立,來提高測試涵蓋率。 19 | 20 | 當你建立一個類別或函式,你應該為每一個它應該有的行為建立單元測試。 最基本你應該 21 | 確保如果你傳遞錯誤的參數給它會發生錯誤,也確保如果你傳遞合法的參數給它會正確運作。 22 | 在開發週期中,這能幫助確保當你之後修改這個類別或函式時,舊功能依然如預期運作。 唯一的替代方案是在 test.php 檔案裡面執行 var_dump(),但這沒有辦法建立一個應用程式 - 不論大的還是小的。 23 | 24 | 單元測試另外一個用處是在貢獻程式碼給開源專案的時候。 如果你可以寫個測試來展示功能壞掉 (就是失敗),然後修復它,並展示測試通過,修補程式更可能會被接受。 如果你執行一個接受 pull requests 的專案,你應該建議這是一個要求。 25 | 26 | [PHPUnit](http://phpunit.de) 是個為 PHP 27 | 應用程式寫單元測試的主流測試框架,但是還有一些其他選擇 28 | 29 | * [atoum](https://github.com/atoum/atoum) 30 | * [Enhance PHP](https://github.com/Enhance-PHP/Enhance-PHP) 31 | * [PUnit](http://punit.smf.me.uk/) 32 | * [SimpleTest](http://simpletest.org) 33 | 34 | 35 | ### 整合測試 36 | 37 | 出自 [維基百科](http://en.wikipedia.org/wiki/Integration_testing): 38 | 39 | > 整合測試 (有時稱作整合和測試,縮寫「I&T」) 是結合個別軟體模組作為一個組合進行測試的軟體測試階段。 它處於單元測試之後和驗證測試之前。 整合測試拿已經做過單元測試的輸入模組,把它們聚集成比較大的項目,應用整合測試計畫來測試這些比較大的項目,並交付輸出整合的系統用來系統測試。 40 | 41 | 因為使用許多同樣的原理,很多可以用來單元測試的工具也可以用來整合測試。 42 | 43 | ### 功能測試 44 | 45 | 有時也被稱為驗收測試,功能測試使用工具來建立自動化的 46 | 測試,並用真實的應用程式進行測試,而不只是驗證個別單元的程式碼運作正確或驗證個別單元可以跟其他單元正確地溝通。 這些工具通常使用真實的資料並模擬應用程式實際的使用者來測試。 47 | 48 | #### 功能測試工具 49 | 50 | * [Selenium](http://seleniumhq.com) 51 | * [Mink](http://mink.behat.org) 52 | * [Codeception](http://codeception.com) 是個包含驗收測試工具的全端測試框架 53 | * [Storyplayer](http://datasift.github.io/storyplayer) 是個包含依照需求建立和破壞測試環境功能的全端測試框架 54 | -------------------------------------------------------------------------------- /_posts/11-03-01-Behavior-Driven-Development.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 行為驅動開發 4 | anchor: behavior_driven_development 5 | --- 6 | 7 | ## 行為驅動開發 {#behavior_driven_development_title} 8 | 9 | 有兩種不同的行為驅動開發 (BDD): SpecBDD 和 StoryBDD。 SpecBDD 專注於程式碼技術上的行為,而 StoryBDD 專注於商業或功能的行為和互動。 這兩種 BDD 都有 PHP 的框架。 10 | 11 | 採用 StoryBDD 時,你撰寫人可以閱讀的故事來描述應用程式的行為。 接著這些故事 12 | 可以作為應用程式的實際測試案例執行。 使用在 PHP 應用程式的 StoryBDD 框架 13 | 是 Behat,它受到 Ruby 的 [Cucumber](http://cukes.info/) 專案啟發並實作了 Gherkin DSL 14 | 來描述功能的行為。 15 | 16 | 採用 SpecBDD 時,你撰寫規格來描述實際的程式碼應該有什麼行為。 描述函式或方法應該有什麼行為,而不是測試函式或方法。 PHP 提供 PHPSpec 框架來達到這個目的。 這個框架受到 Ruby 的 [RSpec 專案](http://rspec.info/) 啟發。 17 | 18 | ### BDD 連結 19 | 20 | * [Behat](http://behat.org/),PHP 的 StoryBDD 框架,受到 Ruby 的 [Cucumber](http://cukes.info/) 專案啟發。 21 | * [PHPSpec](http://www.phpspec.net/),PHP 的 SpecBDD 框架,受到 Ruby 的 [RSpec](http://rspec.info/) 專案啟發。 22 | * [Codeception](http://www.codeception.com) 是個使用 BDD 原則的全端測試框架。 23 | -------------------------------------------------------------------------------- /_posts/11-04-01-Complementary-Testing-Tools.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 其他測試工具 4 | anchor: complementary_testing_tools 5 | --- 6 | 7 | ## 其他測試工具 {#complementary_testing_tools_title} 8 | 9 | 除了個別的測試驅動和行為驅動框架之外,還有一些通用的框架和輔助函式庫,對任何的測試方法都很有用。 10 | 11 | ### 工具的超連結 12 | 13 | * [Selenium](http://seleniumhq.org/) 是個瀏覽器自動化工具,可以 [跟 PHPUnit 整合](http://phpunit.de/manual/current/en/selenium.html) 14 | * [Mockery](https://github.com/padraic/mockery) 是個可以跟 [PHPUnit](http://phpunit.de/) 或 [PHPSpec](http://www.phpspec.net/) 整合的 Mock 物件框架 15 | * [Prophecy](https://github.com/phpspec/prophecy) 是個 有自己的想法,然而非常強大且靈活的 PHP 物件 mocking 框架。 它整合了 [PHPSpec](http://www.phpspec.net/) 並且可以跟 [PHPUnit](http://phpunit.de/) 一起使用。 16 | -------------------------------------------------------------------------------- /_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 | isChild: true 3 | title: Platform as a Service (PaaS) 4 | anchor: platform_as_a_service 5 | --- 6 | 7 | ## Platform as a Service (PaaS) {#platform_as_a_service_title} 8 | 9 | PaaS 在 web 上提供了 PHP 應用程式所需的系統和網路架構。意即不需要提供設定檔發佈 PHP 應用程式或框架。 10 | 11 | 最近 PaaS 成為用以部署,以及擴展不同大小的 PHP 應用程式的一種流行方法。你可以在我們的 [資源一覽](#resources) 找到 [PHP PaaS "Platform as a Service" 供應商](#php-paas-providers) 的列表。 12 | -------------------------------------------------------------------------------- /_posts/12-03-01-Virtual-or-Dedicated-Servers.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 虛擬或自架伺服器 4 | anchor: virtual_or_dedicated_servers 5 | --- 6 | 7 | ## 虛擬或自架伺服器 {#virtual_or_dedicated_servers_title} 8 | 9 | 如果你傾向自己管理系統,或是有興趣學習,虛擬或自己的伺服器環境可以讓你自己控制應用程式的上線環境。 10 | 11 | ### nginx 和 PHP-FPM 12 | 13 | PHP 透過內建的 FastCGI Process Manager( FPM ),可以和 [nginx](http://nginx.org) 搭配的很好( nginx 是個輕量,高效能的伺服器)。它比 Apache 消耗更少的記憶體,且可以處理更多並行請求。這在沒有太多記憶體空間可用虛擬機器裡尤其重要。 14 | 15 | * [更多關於 nginx](http://nginx.org) 16 | * [更多關於 PHP-FPM](http://php.net/manual/en/install.fpm.php) 17 | * [關於安全設定 nginx 和 PHP-FPM](https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/) 18 | 19 | ### Apache 和 PHP 20 | 21 | PHP 和 Apache 的搭配使用已經蠻久了, Apache 可以進行高度客製並且有很多[模組](http://httpd.apache.org/docs/2.4/mod/) 可以擴展功能。對於共用伺服器,或想簡單設定 PHP 框架,以及使用開源程式像 WordPress,它是個流行的選擇。不幸的是, Apache 先天上要比 nginx 耗費更多資源並且無法同時應付更多的使用者。 22 | 23 | Apache 有很多設定方式可以執行 PHP 。最普遍且最簡單的設定方式是使用 [prefork MPM](http://httpd.apache.org/docs/2.4/mod/prefork.html) 加上 mod_php5 。雖然它不是對記憶體最高效的方式,但它是最簡單使用的方式。如果你不想在伺服器系統管理方面太過深入,這可能是最好的選擇。注意如果你使用 mod_php5 ,你也必須使用 prefork MPM。 24 | 25 | 另外,如果你想讓 Apache 有更好的效能和更多的穩定性,那麼可以使用像是 nginx FPM 的 [worker MPM](http://httpd.apache.org/docs/2.4/mod/worker.html) 或是 [event MPM](http://httpd.apache.org/docs/2.4/mod/event.html) 搭配 mod_fastcgi 或 mod_fcgid 。兩者在設定上對於記憶體使用更為高效並且更快,但是設定上需要花更多功夫。 26 | 27 | * [更多關於 Apache](http://httpd.apache.org/) 28 | * [更多關於 Multi-Processing Modules](http://httpd.apache.org/docs/2.4/mod/mpm_common.html) 29 | * [更多關於 mod_fastcgi](http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html) 30 | * [更多關於 mod_fcgid](http://httpd.apache.org/mod_fcgid/) 31 | -------------------------------------------------------------------------------- /_posts/12-04-01-Shared-Servers.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 共用伺服器 4 | anchor: shared_servers 5 | --- 6 | 7 | ## 共用伺服器 {#shared_servers_title} 8 | 9 | PHP 相當普遍,很少有伺服器沒有內建 PHP ,但是要確認它是最新的版本。 共用伺服器讓你可以讓你和其他開發者在同一台機器上部署網站。好處是這已經是一種便宜的方式,壞處是 10 | 伺服器的負載可能下降或是有安全性漏洞是主要考量。如果預算足夠,你應該要避開它。 11 | -------------------------------------------------------------------------------- /_posts/12-05-01-Building-your-Application.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 建立及部署應用程式 4 | anchor: building_and_deploying_your_application 5 | --- 6 | 7 | ## 建立及部署應用程式 {#building_and_deploying_your_application_title} 8 | 9 | 如果你發現自己在(手動)上傳檔案前,需要手動處理資料庫 schema 改變,或是手動執行測試,先想一想!在更新版本時,每個額外的人工部署任務都會增加嚴重錯誤的可能性。當你進行簡單的更新時,或是建構網站,或是持續整合( continuous integration )策略, [自動化部署](http://en.wikipedia.org/wiki/Build_automation) 會是你的好朋友。 10 | 11 | 你可能會想要自動化的任務有: 12 | 13 | * 相依性管理 14 | * 編譯,最小化 assets 15 | * 執行測試 16 | * 建立文件 17 | * 打包 18 | * 部署 19 | 20 | 21 | ### Build Automation Tools 22 | 23 | Build tools can be described as a collection of scripts that handle common tasks of software deployment. The build 24 | tool is not a part of your software, it acts on your software from 'outside'. 25 | 26 | There are many open source tools available to help you with build automation, some are written in PHP others aren't. 27 | This shouldn't hold you back from using them, if they're better suited for the specific job. Here are a few examples: 28 | 29 | [Phing](http://www.phing.info/) is the easiest way to get started with automated deployment in the PHP world. With 30 | Phing you can control your packaging, deployment or testing process from within a simple XML build file. Phing (which 31 | is based on [Apache Ant](http://ant.apache.org/)) provides a rich set of tasks usually needed to install or update a 32 | web app and can be extended with additional custom tasks, written in PHP. 33 | 34 | [Capistrano](https://github.com/capistrano/capistrano/wiki) is a system for *intermediate-to-advanced programmers* to 35 | execute commands in a structured, repeatable way on one or more remote machines. It is pre-configured for deploying 36 | Ruby on Rails applications, however people are **successfully deploying PHP systems** with it. Successful use of 37 | Capistrano depends on a working knowledge of Ruby and Rake. 38 | 39 | Dave Gardner's blog post [PHP Deployment with Capistrano](http://www.davegardner.me.uk/blog/2012/02/13/php-deployment-with-capistrano/) 40 | is a good starting point for PHP developers interested in Capistrano. 41 | 42 | [Chef](http://www.opscode.com/chef/) is more than a deployment framework, it is a very powerful Ruby based system 43 | integration framework that doesn't just deploy your app but can build your whole server environment or virtual boxes. 44 | 45 | Chef resources for PHP developers: 46 | 47 | * [Three part blog series about deploying a LAMP application with Chef, Vagrant, and EC2](http://www.jasongrimes.org/2012/06/managing-lamp-environments-with-chef-vagrant-and-ec2-1-of-3/) 48 | * [Chef Cookbook which installs and configures PHP 5.3 and the PEAR package management system](https://github.com/opscode-cookbooks/php) 49 | * [Chef video tutorial series by Opscode, the makers of chef](https://www.youtube.com/playlist?list=PLrmstJpucjzWKt1eWLv88ZFY4R1jW8amR) 50 | Further reading: 51 | 52 | * [Automate your project with Apache Ant](http://net.tutsplus.com/tutorials/other/automate-your-projects-with-apache-ant/) 53 | 54 | ### Continuous Integration 55 | 56 | > Continuous Integration is a software development practice where members of a team integrate their work frequently, 57 | > usually each person integrates at least daily — leading to multiple integrations per day. Many teams find that this 58 | > approach leads to significantly reduced integration problems and allows a team to develop cohesive software more 59 | > rapidly. 60 | 61 | *-- Martin Fowler* 62 | 63 | There are different ways to implement continuous integration for PHP. Recently [Travis CI](https://travis-ci.org/) has 64 | done a great job of making continuous integration a reality even for small projects. Travis CI is a hosted continuous 65 | integration service for the open source community. It is integrated with GitHub and offers first class support for many 66 | languages including PHP. 67 | 68 | Further reading: 69 | 70 | * [Continuous Integration with Jenkins](http://jenkins-ci.org/) 71 | * [Continuous Integration with PHPCI](http://www.phptesting.org/) 72 | * [Continuous Integration with Teamcity](http://www.jetbrains.com/teamcity/) 73 | -------------------------------------------------------------------------------- /_posts/13-01-01-Caching.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 快取 3 | anchor: caching 4 | --- 5 | 6 | # 快取 {#caching_title} 7 | 8 | PHP 本身非常快速,但是在遇到諸如遠端連線,載入檔案時會有瓶頸。好佳在,有很多工具可以加速這些部分的處理,或是減少進行這些耗時任務的次數。 9 | -------------------------------------------------------------------------------- /_posts/13-02-01-Bytecode-Cache.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: Bytecode 快取 4 | anchor: bytecode_cache 5 | --- 6 | 7 | ## Bytecode 快取 {#bytecode_cache_title} 8 | 9 | 當 PHP 檔案被執行時,會先被編碼成 bytecode ,(也被稱為 opcode ),然後才會執行這些 bytecode。如果 PHP 檔案沒有被修改,編成的 bytecode 也不會改變。這意味這編碼這一步驟是浪費 CPU 資源的。 10 | 11 | 這就是 Bytecode 快取的用途。它會將編好 bytecode 存在記憶體,並且在往後的呼叫中重複使用,以防止多餘的編譯。 12 | 設定 bytecode 快取僅需要花上幾分鐘,然後應用程式的執行速度就能得到飛快的提升。實在沒有理由不使用它。 13 | 14 | 在 PHP 5.5 之後,內建有 bytecode 快取, [OPcache](http://php.net/manual/en/book.opcache.php) ,更早的版本也可以使用。 15 | 16 | 其他流行的 bytecodes 快取: 17 | 18 | * [APC](http://php.net/manual/en/book.apc.php) (PHP 5.4 and earlier) 19 | * [XCache](http://xcache.lighttpd.net/) 20 | * [Zend Optimizer+](http://www.zend.com/products/server/) (part of Zend Server package) 21 | * [WinCache](http://www.iis.net/download/wincacheforphp) (extension for MS Windows Server) 22 | -------------------------------------------------------------------------------- /_posts/13-03-01-Object-Caching.md: -------------------------------------------------------------------------------- 1 | --- 2 | isChild: true 3 | title: 物件快取 4 | anchor: object_caching 5 | --- 6 | 7 | ## 物件快取 {#object_caching_title} 8 | 9 | 快取程式碼裡面的物件常常是很有助益的,像是取得時很耗資源的資料,或是資料庫裡不太會變動的資料。你可以使用物件快取系統來保存這些資料以供日後快速取用。如果你在取出這些資料後可以儲存它們,那麼在往後的請求裡就可以直接使用這些資料,從而獲得效能的提升並減少資料庫的負擔。 10 | 11 | 有很多流行的 bytecode 快取同時也可以讓你儲存自定義資料,所以這或許是一個更好的理由去使用他們。 PCu , XCache , 和 WinCache 都有 APIs 可以將資料從 PHP 程式碼存到記憶體。 12 | 13 | 最常用的記憶體資料快取系統是 APCu 和 memcached 。 APCu 是儲存資料很好的選擇之一,他有很簡單的 API 讓你把資料加到記憶體快取,並且非常容易設定和使用。但是 APCu 的一個限制是只能用在其安裝的伺服器上。 另一方面, Memcached 可以作為獨立的服務,可以從網路存取,意味著你可以將資料可以存在 hyper-fast 資料儲存中心以及很多不同可以取得資料的系統。 14 | 15 | 注意如果在伺服器跑的是 PHP (Fast-)CGI ,每個 PHP 行程會有自己的快取,也就是說, APCu 的資料不會在工作行程( worker processes )間共用,因為它不會綁定到 PHP 的行程。 16 | 17 | 在單一網路環境內, APCu 的存取速度通常會表現的比 memcached 好,但是 memcached 可以很快速的進行擴展。如果你不打算用多伺服器架設你的應用程式,或是不需要 memcached 特有的功能, APCu 或許是快取資料的最好選擇。 18 | 19 | APCu 的使用範例: 20 | 21 | {% highlight php %} 22 | 16 | * @link http://www.phpdoc.org/docs/latest/index.html 17 | * @package helper 18 | */ 19 | class DateTimeHelper 20 | { 21 | /** 22 | * @param mixed $anything Anything that we can convert to a \DateTime object 23 | * 24 | * @return \DateTime 25 | * @throws \InvalidArgumentException 26 | */ 27 | public function dateTimeFromAnything($anything) 28 | { 29 | $type = gettype($anything); 30 | 31 | switch ($type) { 32 | // Some code that tries to return a \DateTime object 33 | } 34 | 35 | throw new \InvalidArgumentException( 36 | "Failed Converting param of type '{$type}' to DateTime object" 37 | ); 38 | } 39 | 40 | /** 41 | * @param mixed $date Anything that we can convert to a \DateTime object 42 | * 43 | * @return void 44 | */ 45 | public function printISO8601Date($date) 46 | { 47 | echo $this->dateTimeFromAnything($date)->format('c'); 48 | } 49 | 50 | /** 51 | * @param mixed $date Anything that we can convert to a \DateTime object 52 | */ 53 | public function printRFC2822Date($date) 54 | { 55 | echo $this->dateTimeFromAnything($date)->format('r'); 56 | } 57 | } 58 | {% endhighlight %} 59 | 60 | 這個類別說明首先使用 [@author](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/author.html) 標記,它是用來說明程式碼的作者,在多位開發者的情況下,可以同時列出好幾位。其次使用 [@link](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/link.html) 標記,它提供連一個網站連結,連結到進一步說明程式碼的網站。第三個標記使用了 [@package](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/package.html),用來分類組織程式碼。 61 | 62 | 在這個類別中,第一個方法的 [@param](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/param.html) 標記,說明類型、名稱和傳入方法的參數。此外,[@return](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/return.html) 和 [@throws](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/throws.html) 標記說明回傳類型以及可能會丟出的例外。 63 | 64 | 第二、第三個方法非常類似,和第一個方法一樣使用一個 [@param](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/param.html) 標記。第二、和第三個方法之間關鍵差別在註解區塊使用/排除 [@return](http://www.phpdoc.org/docs/latest/references/phpdoc/tags/return.html) 標記。「@return void」標記明確告訴我們沒有回傳值,而過去省略「@return void」宣告也具有相同效果(沒有回傳任何東西)。 65 | -------------------------------------------------------------------------------- /banners.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Website Banners 4 | description: "Spread the word! Use these banner to let new PHP programmers know about PHP: The Right Way" 5 | --- 6 | 7 | # 廣告橫幅 8 | 9 | 利用 _PHP 之道_ 廣告橫幅宣傳!讓 PHP 新手能夠知道哪裡可以找到好的資訊。 10 | 11 | ## Button 1 (120x90) 12 | 13 |

    PHP: The Right Way

    14 | 15 | {% highlight html %} 16 | 17 | PHP: The Right Way 18 | 19 | {% endhighlight %} 20 | 21 | ## Button 2 (120x60) 22 | 23 |

    PHP: The Right Way

    24 | 25 | {% highlight html %} 26 | 27 | PHP: The Right Way 28 | 29 | {% endhighlight %} 30 | 31 | ## Leaderboard (728x90) 32 | 33 |

    PHP: The Right Way

    34 | 35 | {% highlight html %} 36 | 37 | PHP: The Right Way 38 | 39 | {% endhighlight %} 40 | 41 | ## Large Rectangle (386x280) 42 | 43 |

    PHP: The Right Way

    44 | 45 | {% highlight html %} 46 | 47 | PHP: The Right Way 48 | 49 | {% endhighlight %} 50 | 51 | ## Medium Rectangle (300x250) 52 | 53 |

    PHP: The Right Way

    54 | 55 | {% highlight html %} 56 | 57 | PHP: The Right Way 58 | 59 | {% endhighlight %} 60 | 61 | ## Rectangle (180x150) 62 | 63 |

    PHP: The Right Way

    64 | 65 | {% highlight html %} 66 | 67 | PHP: The Right Way 68 | 69 | {% endhighlight %} 70 | 71 | ## Square Button (125x125) 72 | 73 |

    PHP: The Right Way

    74 | 75 | {% highlight html %} 76 | 77 | PHP: The Right Way 78 | 79 | {% endhighlight %} 80 | 81 | ## Vertical Rectangle (240x400) 82 | 83 |

    PHP: The Right Way

    84 | 85 | {% highlight html %} 86 | 87 | PHP: The Right Way 88 | 89 | {% endhighlight %} 90 | -------------------------------------------------------------------------------- /images/banners/btn1-120x90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/images/banners/btn1-120x90.png -------------------------------------------------------------------------------- /images/banners/btn2-120x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/images/banners/btn2-120x60.png -------------------------------------------------------------------------------- /images/banners/leaderboard-728x90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/images/banners/leaderboard-728x90.png -------------------------------------------------------------------------------- /images/banners/lg-rect-386x280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/images/banners/lg-rect-386x280.png -------------------------------------------------------------------------------- /images/banners/med-rect-300x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/images/banners/med-rect-300x250.png -------------------------------------------------------------------------------- /images/banners/rect-180x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/images/banners/rect-180x150.png -------------------------------------------------------------------------------- /images/banners/sq-btn-125x125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/images/banners/sq-btn-125x125.png -------------------------------------------------------------------------------- /images/banners/vert-rect-240x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/images/banners/vert-rect-240x400.png -------------------------------------------------------------------------------- /images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/images/favicon.png -------------------------------------------------------------------------------- /images/nmc-logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/images/nmc-logo.gif -------------------------------------------------------------------------------- /images/og-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laravel-taiwan/php-the-right-way/a2c26dd87dc4325807ed031125f8a96f55081e57/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 | --- 5 | 6 | {% capture welcome_content %}{% include welcome.md %}{% endcapture %} 7 | {{ welcome_content|markdownify }} 8 | 9 | {% capture backtotop %}[Back to Top](#top){:.top}{% endcapture %} 10 | {% for post in site.posts reversed %} 11 | {% if post.isChild != true and loop.first != true %} 12 | {{ backtotop|markdownify }} 13 | {% endif %} 14 |
    15 | {{ post.content }} 16 |
    17 | {% endfor %} 18 | {{ backtotop|markdownify }} 19 | -------------------------------------------------------------------------------- /pages/Design-Patterns.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Design Patterns 4 | --- 5 | 6 | # Design Patterns 7 | 8 | There are numerous ways to structure the code and project for your web application, and you can put as much or as little 9 | thought as you like into architecting. But it is usually a good idea to follow common patterns because it will make 10 | your code easier to manage and easier for others to understand. 11 | 12 | * [Architectural pattern on Wikipedia](https://en.wikipedia.org/wiki/Architectural_pattern) 13 | * [Software design pattern on Wikipedia](https://en.wikipedia.org/wiki/Software_design_pattern) 14 | * [Collection of implementation examples](https://github.com/domnikl/DesignPatternsPHP) 15 | 16 | ## Factory 17 | 18 | One of the most commonly used design patterns is the factory pattern. In this pattern, a class simply creates 19 | the object you want to use. Consider the following example of the factory pattern: 20 | 21 | {% highlight php %} 22 | vehicleMake = $make; 31 | $this->vehicleModel = $model; 32 | } 33 | 34 | public function getMakeAndModel() 35 | { 36 | return $this->vehicleMake . ' ' . $this->vehicleModel; 37 | } 38 | } 39 | 40 | class AutomobileFactory 41 | { 42 | public static function create($make, $model) 43 | { 44 | return new Automobile($make, $model); 45 | } 46 | } 47 | 48 | // have the factory create the Automobile object 49 | $veyron = AutomobileFactory::create('Bugatti', 'Veyron'); 50 | 51 | print_r($veyron->getMakeAndModel()); // outputs "Bugatti Veyron" 52 | {% endhighlight %} 53 | 54 | This code uses a factory to create the Automobile object. There are two possible benefits to building your code this 55 | way; the first is that if you need to change, rename, or replace the Automobile class later on you can do so and you 56 | will only have to modify the code in the factory, instead of every place in your project that uses the Automobile 57 | class. The second possible benefit is that if creating the object is a complicated job you can do all of the work in 58 | the factory, instead of repeating it every time you want to create a new instance. 59 | 60 | Using the factory pattern isn't always necessary (or wise). The example code used here is so simple that a factory 61 | would simply be adding unneeded complexity. However if you are making a fairly large or complex project you may save 62 | yourself a lot of trouble down the road by using factories. 63 | 64 | * [Factory pattern on Wikipedia](https://en.wikipedia.org/wiki/Factory_pattern) 65 | 66 | ## Singleton 67 | 68 | When designing web applications, it often makes sense conceptually and architecturally to allow access to one and 69 | only one instance of a particular class. The singleton pattern enables us to do this. 70 | 71 | {% highlight php %} 72 | output = $outputType; 214 | } 215 | 216 | public function loadOutput() 217 | { 218 | return $this->output->load(); 219 | } 220 | } 221 | {% endhighlight %} 222 | 223 | The calling client class above has a private property which must be set at runtime and be of type 'OutputInterface' 224 | once this property is set a call to loadOutput() will call the load() method in the concrete class of the output type 225 | that has been set. 226 | {% highlight php %} 227 | setOutput(new ArrayOutput()); 233 | $data = $client->loadOutput(); 234 | 235 | // Want some JSON? 236 | $client->setOutput(new JsonStringOutput()); 237 | $data = $client->loadOutput(); 238 | 239 | {% endhighlight %} 240 | 241 | * [Strategy pattern on Wikipedia](http://en.wikipedia.org/wiki/Strategy_pattern) 242 | 243 | ## Front Controller 244 | 245 | The front controller pattern is where you have a single entrance point for you web application (e.g. index.php) that 246 | handles all of the requests. This code is responsible for loading all of the dependencies, processing the request and 247 | sending the response to the browser. The front controller pattern can be beneficial because it encourages modular code 248 | and gives you a central place to hook in code that should be run for every request (such as input sanitization). 249 | 250 | * [Front Controller pattern on Wikipedia](https://en.wikipedia.org/wiki/Front_Controller_pattern) 251 | 252 | ## Model-View-Controller 253 | 254 | The model-view-controller (MVC) pattern and its relatives HMVC and MVVM lets you break up code into logical objects that serve very specific purposes. Models serve as a data access layer where data is fetched and returned in formats usable throughout your application. Controllers handle the request, process the data returned from models and load views to send in the response. And views are display templates (markup, xml, etc) that are sent in the response to the web browser. 255 | 256 | MVC is the most common architectural pattern used in the popular [PHP frameworks](https://github.com/codeguy/php-the-right-way/wiki/Frameworks). 257 | 258 | Learn more about MVC and its relatives: 259 | 260 | * [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93View%E2%80%93Controller) 261 | * [HMVC](https://en.wikipedia.org/wiki/Hierarchical_model%E2%80%93view%E2%80%93controller) 262 | * [MVVM](https://en.wikipedia.org/wiki/Model_View_ViewModel) 263 | -------------------------------------------------------------------------------- /pages/Functional-Programming.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Functional Programming in PHP 4 | --- 5 | 6 | # Functional Programming in PHP 7 | 8 | PHP supports first-class functions, meaning that a function can be assigned to a variable. Both user-defined and built-in 9 | functions can be referenced by a variable and invoked dynamically. Functions can be passed as arguments to other 10 | functions and a function can return other functions (a feature called higher-order functions). 11 | 12 | Recursion, a feature that allows a function to call itself, is supported by the language, but most of the PHP code focus 13 | is on iteration. 14 | 15 | Anonymous functions (with support for closures) have been present since PHP 5.3 (2009). 16 | 17 | PHP 5.4 added the ability to bind closures to an object's scope and also improved support for callables such that they 18 | can be used interchangeably with anonymous functions in almost all cases. 19 | 20 | The most common usage of higher-order functions is when implementing a strategy pattern. The built-in `array_filter` 21 | function asks both for the input array (data) and a function (a strategy or a callback) used as a filter function on 22 | each array item. 23 | 24 | {% highlight php %} 25 | $min 55 | * 56 | * Returns a single filter out of a family of "greater than n" filters 57 | */ 58 | function criteria_greater_than($min) 59 | { 60 | return function($item) use ($min) { 61 | return $item > $min; 62 | }; 63 | } 64 | 65 | $input = array(1, 2, 3, 4, 5, 6); 66 | 67 | // Use array_filter on a input with a selected filter function 68 | $output = array_filter($input, criteria_greater_than(3)); 69 | 70 | print_r($output); // items > 3 71 | {% endhighlight %} 72 | 73 | Each filter function in the family accepts only elements greater than some minimum value. Single filter returned by 74 | `criteria_greater_than` is a closure with `$min` argument closed by the value in the scope (given as an argument when 75 | `criteria_greater_than` is called). 76 | 77 | Early binding is used by default for importing `$min` variable into the created function. For true closures with late 78 | binding one should use a reference when importing. Imagine a templating or input validation library, where closure is 79 | defined to capture variables in scope and access them later when the anonymous function is evaluated. 80 | 81 | * [Read about Anonymous functions][anonymous-functions] 82 | * [More details in the Closures RFC][closures-rfc] 83 | * [Read about dynamically invoking functions with `call_user_func_array`][call-user-func-array] 84 | 85 | [anonymous-functions]: http://www.php.net/manual/en/functions.anonymous.php 86 | [call-user-func-array]: http://php.net/manual/en/function.call-user-func-array.php 87 | [closures-rfc]: https://wiki.php.net/rfc/closures 88 | -------------------------------------------------------------------------------- /pages/The-Basics.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: The Basics 4 | --- 5 | 6 | # The Basics 7 | 8 | ## Comparison operators 9 | 10 | Comparison operators are an often overlooked aspect of PHP, which can lead to many unexpected outcomes. One such 11 | problem stems from strict comparisons (the comparison of booleans as integers). 12 | 13 | {% highlight php %} 14 | This project would not be possible without the help of our amazing contributors on GitHub.

    '); 7 | }; 8 | $.ajax({ 9 | cache: false, 10 | dataType: 'jsonp', 11 | timeout: 3000, 12 | type: 'GET', 13 | url: 'https://api.github.com/repos/laravel-taiwan/php-the-right-way/contributors?per_page=100' 14 | }).done(function (data) { 15 | if ( data.data && data.data.length ) { 16 | var $ul = $('
      '), dataLength = data.data.length; 17 | for ( var i = 0; i < dataLength; i++ ) { 18 | $ul.append(['
    • ', data.data[i].login, '
    • '].join('')); 19 | } 20 | $contributors.html($ul); 21 | } else { 22 | fail(); 23 | } 24 | }).fail(fail); 25 | } 26 | })(jQuery); 27 | 28 | (function ($) { 29 | //Add current view's highlighting to the navigation 30 | 31 | /** helper for highlighting */ 32 | function highlightNav(navLinks,id) 33 | { 34 | navLinks.filter('[href="/php-the-right-way/#'+id+'"]').addClass("active"); 35 | } 36 | 37 | $(window).scroll(function() { 38 | //console.log("They see me scrollin, they hatin"); 39 | 40 | //clear highlighting 41 | var navLinks = $('.site-navigation a'); 42 | navLinks.removeClass("active"); 43 | 44 | //calc current viewport 45 | var viewTop = $(window).scrollTop(); 46 | var viewBottom = viewTop + $(window).height(); 47 | 48 | //for all h1 and h2 elements, check if they are visible 49 | //performance tweak: stop each() after the first element is found to be behind view 50 | var previous = ""; 51 | var foundOne = false; 52 | var fallback = ""; 53 | $('h1, h2').each(function(i,e) { 54 | //get element position; 55 | var eTop = $(e).offset().top; 56 | var eBottom = eTop + $(e).height(); 57 | var id=e.id; 58 | id = id.replace("_title", ""); 59 | 60 | if (eTop >= viewTop) { 61 | //if we are passed the view and no heading was highlighted yet, store previous one as fallback 62 | if (! foundOne) { 63 | fallback=previous; 64 | } 65 | if (eBottom <= viewBottom) { 66 | highlightNav(navLinks, id); 67 | foundOne = true; 68 | } else { 69 | return false; //break the each(), the rest is below 70 | } 71 | } 72 | previous=id; 73 | }); 74 | //no h1/h2 is in the viewport, so highlight the last one above 75 | if (! foundOne) { 76 | highlightNav(navLinks, fallback); 77 | } 78 | }); 79 | })(jQuery); 80 | 81 | -------------------------------------------------------------------------------- /sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | http://www.phptherightway.com/index.html 5 | 2012-07-07T01:00:00-05:00 6 | daily 7 | 1 8 | 9 | 10 | http://www.phptherightway.com/banners.html 11 | 2012-07-08T14:11:00-05:00 12 | weekly 13 | 0.5 14 | 15 | 16 | -------------------------------------------------------------------------------- /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/prefixer.less: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------- 2 | LESS Prefixer 3 | --------------------------------------------------- 4 | 5 | All of the CSS3 fun, none of the prefixes! 6 | 7 | As a rule, you can use the CSS properties you 8 | would expect just by adding a '.': 9 | 10 | box-shadow => .box-shadow(@args) 11 | 12 | Also, when shorthand is available, arguments are 13 | not parameterized. Learn CSS, not LESS Prefixer. 14 | 15 | ------------------------------------------------- 16 | TABLE OF CONTENTS 17 | (*) denotes a syntax-sugar helper 18 | ------------------------------------------------- 19 | 20 | .animation(@args) 21 | .animation-delay(@delay) 22 | .animation-direction(@direction) 23 | .animation-duration(@duration) 24 | .animation-iteration-count(@count) 25 | .animation-name(@name) 26 | .animation-play-state(@state) 27 | .animation-timing-function(@function) 28 | .background-size(@args) 29 | .border-radius(@args) 30 | .box-shadow(@args) 31 | .inner-shadow(@args) * 32 | .box-sizing(@args) 33 | .border-box() * 34 | .content-box() * 35 | .columns(@args) 36 | .column-count(@count) 37 | .column-gap(@gap) 38 | .column-rule(@args) 39 | .column-width(@width) 40 | .gradient(@default,@start,@stop) * 41 | .linear-gradient-top(@default,@color1,@stop1,@color2,@stop2,[@color3,@stop3,@color4,@stop4])* 42 | .linear-gradient-left(@default,@color1,@stop1,@color2,@stop2,[@color3,@stop3,@color4,@stop4])* 43 | .opacity(@factor) 44 | .transform(@args) 45 | .rotate(@deg) 46 | .scale(@factor) 47 | .translate(@x,@y) 48 | .translate3d(@x,@y,@z) 49 | .translateHardware(@x,@y) * 50 | .text-shadow(@args) 51 | .transition(@args) 52 | .transition-delay(@delay) 53 | .transition-duration(@duration) 54 | .transition-property(@property) 55 | .transition-timing-function(@function) 56 | 57 | 58 | 59 | Credit to LESS Elements for the motivation and 60 | to CSS3Please.com for implementation. 61 | 62 | Copyright (c) 2012 Joel Sutherland 63 | MIT Licensed: 64 | http://www.opensource.org/licenses/mit-license.php 65 | 66 | -----------------------------------------------------*/ 67 | 68 | 69 | /* Animation */ 70 | .animation(@args) { 71 | -webkit-animation: @args; 72 | -moz-animation: @args; 73 | -ms-animation: @args; 74 | -o-animation: @args; 75 | } 76 | .animation-delay(@delay) { 77 | -webkit-animation-delay: @delay; 78 | -moz-animation-delay: @delay; 79 | -ms-animation-delay: @delay; 80 | -o-animation-delay: @delay; 81 | } 82 | .animation-direction(@direction) { 83 | -webkit-animation-direction: @direction; 84 | -moz-animation-direction: @direction; 85 | -ms-animation-direction: @direction; 86 | -o-animation-direction: @direction; 87 | } 88 | .animation-duration(@duration) { 89 | -webkit-animation-duration: @duration; 90 | -moz-animation-duration: @duration; 91 | -ms-animation-duration: @duration; 92 | -o-animation-duration: @duration; 93 | } 94 | .animation-iteration-count(@count) { 95 | -webkit-animation-iteration-count: @count; 96 | -moz-animation-iteration-count: @count; 97 | -ms-animation-iteration-count: @count; 98 | -o-animation-iteration-count: @count; 99 | } 100 | .animation-name(@name) { 101 | -webkit-animation-name: @name; 102 | -moz-animation-name: @name; 103 | -ms-animation-name: @name; 104 | -o-animation-name: @name; 105 | } 106 | .animation-play-state(@state) { 107 | -webkit-animation-play-state: @state; 108 | -moz-animation-play-state: @state; 109 | -ms-animation-play-state: @state; 110 | -o-animation-play-state: @state; 111 | } 112 | .animation-timing-function(@function) { 113 | -webkit-animation-timing-function: @function; 114 | -moz-animation-timing-function: @function; 115 | -ms-animation-timing-function: @function; 116 | -o-animation-timing-function: @function; 117 | } 118 | 119 | 120 | /* Background Size */ 121 | 122 | .background-size(@args) { 123 | -webkit-background-size: @args; 124 | -moz-background-size: @args; 125 | background-size: @args; 126 | } 127 | 128 | 129 | /* Border Radius */ 130 | 131 | .border-radius(@args) { 132 | -webkit-border-radius: @args; 133 | -moz-border-radius: @args; 134 | border-radius: @args; 135 | 136 | -webkit-background-clip: padding-box; 137 | -moz-background-clip: padding; 138 | background-clip: padding-box; 139 | } 140 | 141 | 142 | /* Box Shadows */ 143 | .box-shadow(@args) { 144 | -webkit-box-shadow: @args; 145 | -moz-box-shadow: @args; 146 | box-shadow: @args; 147 | } 148 | .inner-shadow(@args) { 149 | .box-shadow(inset @args); 150 | } 151 | 152 | 153 | /* Box Sizing */ 154 | .box-sizing(@args){ 155 | -webkit-box-sizing: @args; 156 | -moz-box-sizing: @args; 157 | box-sizing: @args; 158 | } 159 | .border-box(){ 160 | .box-sizing(border-box); 161 | } 162 | .content-box(){ 163 | .box-sizing(content-box); 164 | } 165 | 166 | 167 | 168 | /* Columns */ 169 | .columns(@args){ 170 | -webkit-columns: @args; 171 | -moz-columns: @args; 172 | columns: @args; 173 | } 174 | .column-count(@count) { 175 | -webkit-column-count: @count; 176 | -moz-column-count: @count; 177 | column-count: @count; 178 | } 179 | .column-gap(@gap) { 180 | -webkit-column-gap: @gap; 181 | -moz-column-gap: @gap; 182 | column-gap: @gap; 183 | } 184 | .column-width(@width){ 185 | -webkit-column-width: @width; 186 | -moz-column-width: @width; 187 | column-width: @width; 188 | } 189 | .column-rule(@args){ 190 | -webkit-column-rule: @rule; 191 | -moz-column-rule: @rule; 192 | column-rule: @rule; 193 | } 194 | 195 | 196 | /* Gradients */ 197 | .gradient(@default: #F5F5F5, @start: #EEE, @stop: #FFF) { 198 | .linear-gradient-top(@default,@start,0%,@stop,100%); 199 | } 200 | .linear-gradient-top(@default,@color1,@stop1,@color2,@stop2) { 201 | background-color: @default; 202 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(@stop1, @color1), color-stop(@stop2 @color2)); 203 | background-image: -webkit-linear-gradient(top, @color1 @stop1, @color2 @stop2); 204 | background-image: -moz-linear-gradient(top, @color1 @stop1, @color2 @stop2); 205 | background-image: -ms-linear-gradient(top, @color1 @stop1, @color2 @stop2); 206 | background-image: -o-linear-gradient(top, @color1 @stop1, @color2 @stop2); 207 | background-image: linear-gradient(top, @color1 @stop1, @color2 @stop2); 208 | } 209 | .linear-gradient-top(@default,@color1,@stop1,@color2,@stop2,@color3,@stop3) { 210 | background-color: @default; 211 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(@stop1, @color1), color-stop(@stop2 @color2), color-stop(@stop3 @color3)); 212 | background-image: -webkit-linear-gradient(top, @color1 @stop1, @color2 @stop2, @color3 @stop3); 213 | background-image: -moz-linear-gradient(top, @color1 @stop1, @color2 @stop2, @color3 @stop3); 214 | background-image: -ms-linear-gradient(top, @color1 @stop1, @color2 @stop2, @color3 @stop3); 215 | background-image: -o-linear-gradient(top, @color1 @stop1, @color2 @stop2, @color3 @stop3); 216 | background-image: linear-gradient(top, @color1 @stop1, @color2 @stop2, @color3 @stop3); 217 | } 218 | .linear-gradient-top(@default,@color1,@stop1,@color2,@stop2,@color3,@stop3,@color4,@stop4) { 219 | background-color: @default; 220 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(@stop1, @color1), color-stop(@stop2 @color2), color-stop(@stop3 @color3), color-stop(@stop4 @color4)); 221 | background-image: -webkit-linear-gradient(top, @color1 @stop1, @color2 @stop2, @color3 @stop3, @color4 @stop4); 222 | background-image: -moz-linear-gradient(top, @color1 @stop1, @color2 @stop2, @color3 @stop3, @color4 @stop4); 223 | background-image: -ms-linear-gradient(top, @color1 @stop1, @color2 @stop2, @color3 @stop3, @color4 @stop4); 224 | background-image: -o-linear-gradient(top, @color1 @stop1, @color2 @stop2, @color3 @stop3, @color4 @stop4); 225 | background-image: linear-gradient(top, @color1 @stop1, @color2 @stop2, @color3 @stop3, @color4 @stop4); 226 | } 227 | .linear-gradient-left(@default,@color1,@stop1,@color2,@stop2) { 228 | background-color: @default; 229 | background-image: -webkit-gradient(linear, left top, left top, color-stop(@stop1, @color1), color-stop(@stop2 @color2)); 230 | background-image: -webkit-linear-gradient(left, @color1 @stop1, @color2 @stop2); 231 | background-image: -moz-linear-gradient(left, @color1 @stop1, @color2 @stop2); 232 | background-image: -ms-linear-gradient(left, @color1 @stop1, @color2 @stop2); 233 | background-image: -o-linear-gradient(left, @color1 @stop1, @color2 @stop2); 234 | background-image: linear-gradient(left, @color1 @stop1, @color2 @stop2); 235 | } 236 | .linear-gradient-left(@default,@color1,@stop1,@color2,@stop2,@color3,@stop3) { 237 | background-color: @default; 238 | background-image: -webkit-gradient(linear, left top, left top, color-stop(@stop1, @color1), color-stop(@stop2 @color2), color-stop(@stop3 @color3)); 239 | background-image: -webkit-linear-gradient(left, @color1 @stop1, @color2 @stop2, @color3 @stop3); 240 | background-image: -moz-linear-gradient(left, @color1 @stop1, @color2 @stop2, @color3 @stop3); 241 | background-image: -ms-linear-gradient(left, @color1 @stop1, @color2 @stop2, @color3 @stop3); 242 | background-image: -o-linear-gradient(left, @color1 @stop1, @color2 @stop2, @color3 @stop3); 243 | background-image: linear-gradient(left, @color1 @stop1, @color2 @stop2, @color3 @stop3); 244 | } 245 | .linear-gradient-left(@default,@color1,@stop1,@color2,@stop2,@color3,@stop3,@color4,@stop4) { 246 | background-color: @default; 247 | background-image: -webkit-gradient(linear, left top, left top, color-stop(@stop1, @color1), color-stop(@stop2 @color2), color-stop(@stop3 @color3), color-stop(@stop4 @color4)); 248 | background-image: -webkit-linear-gradient(left, @color1 @stop1, @color2 @stop2, @color3 @stop3, @color4 @stop4); 249 | background-image: -moz-linear-gradient(left, @color1 @stop1, @color2 @stop2, @color3 @stop3, @color4 @stop4); 250 | background-image: -ms-linear-gradient(left, @color1 @stop1, @color2 @stop2, @color3 @stop3, @color4 @stop4); 251 | background-image: -o-linear-gradient(left, @color1 @stop1, @color2 @stop2, @color3 @stop3, @color4 @stop4); 252 | background-image: linear-gradient(left, @color1 @stop1, @color2 @stop2, @color3 @stop3, @color4 @stop4); 253 | } 254 | 255 | 256 | /* Opacity */ 257 | .opacity(@factor){ 258 | opacity: @factor; 259 | @iefactor: @factor*100; 260 | filter: alpha(opacity=@iefactor); 261 | } 262 | 263 | 264 | /* Text Shadow */ 265 | .text-shadow(@args){ 266 | text-shadow: @args; 267 | } 268 | 269 | /* Transforms */ 270 | 271 | .transform(@args) { 272 | -webkit-transform: @args; 273 | -moz-transform: @args; 274 | -ms-transform: @args; 275 | -o-transform: @args; 276 | transform: @args; 277 | } 278 | .rotate(@deg:45deg){ 279 | .transform(rotate(@deg)); 280 | } 281 | .scale(@factor:.5){ 282 | .transform(scale(@factor)); 283 | } 284 | .translate(@x,@y){ 285 | .transform(translate(@x,@y)); 286 | } 287 | .translate3d(@x,@y,@z) { 288 | .transform(translate3d(@x,@y,@z)); 289 | } 290 | .translateHardware(@x,@y){ 291 | .translate(@x,@y); 292 | -webkit-transform: translate3d(@x,@y,0); 293 | -moz-transform: translate3d(@x,@y,0); 294 | } 295 | 296 | 297 | /* Transitions */ 298 | 299 | .transition(@args:200ms) { 300 | -webkit-transition: @args; 301 | -moz-transition: @args; 302 | -o-transition: @args; 303 | transition: @args; 304 | } 305 | .transition-delay(@delay:0) { 306 | -webkit-transition-delay: @delay; 307 | -moz-transition-delay: @delay; 308 | -o-transition-delay: @delay; 309 | transition-delay: @delay; 310 | } 311 | .transition-duration(@duration:200ms) { 312 | -webkit-transition-duration: @duration; 313 | -moz-transition-duration: @duration; 314 | -o-transition-duration: @duration; 315 | transition-duration: @duration; 316 | } 317 | .transition-property(@property:all) { 318 | -webkit-transition-property: @property; 319 | -moz-transition-property: @property; 320 | -o-transition-property: @property; 321 | transition-property: @property; 322 | } 323 | .transition-timing-function(@function:ease) { 324 | -webkit-transition-timing-function: @function; 325 | -moz-transition-timing-function: @function; 326 | -o-transition-timing-function: @function; 327 | transition-timing-function: @function; 328 | } -------------------------------------------------------------------------------- /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: 0px !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: 0px !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: 0px !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: 0px !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: 0px !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: 0px !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: 0px !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: 0px !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: 0px !important; 131 | } -------------------------------------------------------------------------------- /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; } 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 */ --------------------------------------------------------------------------------