23 | * [https://designpatternsphp.readthedocs.io/](https://designpatternsphp.readthedocs.io/en/latest/) ([PDF download](https://www.computer-pdf.com/web-programming/php/924-tutorial-designpatternsphp-documentation.html))
24 |
--------------------------------------------------------------------------------
/_posts/05-05-01-PHP-and-UTF8.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: UTF-8の扱い
3 | isChild: true
4 | anchor: php_and_utf8
5 | ---
6 |
7 | ## UTF-8の扱い {#php_and_utf8_title}
8 |
9 | _このセクションは、もともと[Alex Cabal](https://alexcabal.com/)が
10 | [PHP Best Practices](https://phpbestpractices.org/#utf-8)向けに書いたものだ。この記事をもとに、UTF-8について説明する。_
11 |
12 | ### 一発で済ませる方法はない。注意して、きちんと一貫性を保つこと。
13 |
14 | 今のところPHPは、低レベルではUnicodeをサポートしていない。
15 | PHPでUTF-8文字列をきちんと処理する方法もあるにはあるが、簡単ではない。さらに、ウェブアプリケーションのあらゆるレベル
16 | (つまり、HTMLやSQLからPHPまで)に手を入れる必要がある。
17 | ここでは、それらについて、現実的な範囲で手短にまとめた。
18 |
19 | ### PHPレベルでのUTF-8
20 |
21 | 文字列の連結や変数への代入などの基本操作については、UTF-8だからといって何か特別なことをする必要はない。
22 | しかし、大半の文字列関数(`strpos()` や `strlen()` など)については、そういうわけにはいかない。
23 | これらの関数には、対応する関数として `mb_*` が用意されていることが多い。
24 | たとえば `mb_strpos()` や `mb_strlen()` だ。
25 | これらの `mb_*` 関数は [マルチバイト文字列拡張モジュール] が提供するもので、
26 | Unicode文字列を扱えるように設計されている。
27 |
28 | Unicode文字列を扱う場合は、常に `mb_*` 関数を使う必要がある。
29 | たとえば、UTF-8文字列に対して `substr()` を使うと、その結果の中に文字化けした半角文字が含まれてしまう可能性がある。
30 | この場合、マルチバイト文字列のときに使うべき関数は `mb_substr()` だ。
31 |
32 | 常に `mb_*` 関数を使うように覚えておくのが大変なところだ。
33 | たとえ一か所でもそれを忘れてしまうと、それ以降の Unicode 文字列は壊れてしまう可能性がある。
34 |
35 | そして、すべての文字列関数に `mb_*` 版があるわけではない。
36 | 自分が使いたい関数にマルチバイト版がないだって?
37 | ご愁傷様。
38 |
39 | すべてのPHPスクリプトの先頭(あるいは、グローバルにインクルードするファイルの先頭)で `mb_internal_encoding()`
40 | 関数を使わないといけないし、スクリプトの中でブラウザに出力するつもりなら、それだけではなく
41 | `mb_http_output()` 関数も使わなければいけない。
42 | すべてのスクリプトでエンコーディングを明示しておけば、後で悩まされることもなくなるだろう。
43 |
44 | さらに、文字列を操作する関数の多くには、文字エンコーディングを指定するためのオプション引数が用意されている。
45 | このオプションがある場合は、常に UTF-8 を明示しておくべきだ。
46 | たとえば `htmlentities()` には文字エンコーディングを設定する引数があるので、
47 | UTF-8 文字列を扱うなら常にそう指定しておかないといけない。
48 | PHP 5.4.0 以降では、 `htmlentities()` や `htmlspecialchars()` のデフォルトエンコーディングが UTF-8 に変わった。
49 |
50 | 最後に、他の人たち向けに配布するつもりのアプリケーションなど、その実行環境で `mbstring` が使えるかどうか定かではない場合は、
51 | Composer の [symfony/polyfill-mbstring] パッケージを使うことも検討しよう。
52 | これは、もし `mbstring` があればそれを使い、なければ非 UTF-8 関数にフォールバックするというものだ。
53 |
54 | [マルチバイト文字列拡張モジュール]: https://www.php.net/book.mbstring
55 | [symfony/polyfill-mbstring]: https://packagist.org/packages/symfony/polyfill-mbstring
56 |
57 | ### データベースレベルでのUTF-8
58 |
59 | PHP スクリプトから MySQL に接続する場合は、上で書いた注意をすべて守ったにもかかわらず UTF-8
60 | 文字列がそれ以外のエンコーディングで格納されてしまう可能性がある。
61 |
62 | PHP から MySQL に渡す文字列を確実に UTF-8 として扱わせるには、データベースとテーブルの文字セットや照合順序の設定を、すべて
63 | `utf8mb4` にしておく必要がある。さらに、PDO の接続文字列にも、文字セットとして `utf8mb4` を指定する。
64 | 詳細は以下のコードを参照すること。 _これ、試験に出るよ。_
65 |
66 | UTF-8 を完全にサポートするには、文字セット `utf8mb4` を使わないといけない。 `utf8` はダメ!!!
67 | その理由が知りたければ「あわせて読みたい」を参照すること。
68 |
69 | ### ブラウザレベルでのUTF-8
70 |
71 | `mb_http_output()` 関数を使えば、PHP スクリプトからブラウザへの出力が UTF-8 文字列になることを保証できる。
72 |
73 | あとは、HTTPレスポンスの中で、そのページをUTF-8として扱うようブラウザに指示する必要がある。
74 | いまどきなら、普通はHTTPレスポンスヘッダの中でこんなふうに設定するだろう。
75 |
76 | {% highlight php %}
77 | ` タグの中で [charset の `` タグ](http://htmlpurifier.org/docs/enduser-utf8.html) を指定したりしていたものだ。
82 |
83 | {% highlight php %}
84 | PDO::ERRMODE_EXCEPTION,
111 | PDO::ATTR_PERSISTENT => false
112 | )
113 | );
114 |
115 | // 変換した文字列を、UTF-8としてデータベースに格納する。
116 | // DBとテーブルの文字セットや照合順序が、ちゃんとutf8mb4になっているかな?
117 | $handle = $link->prepare('insert into ElvishSentences (Id, Body, Priority) values (default, :body, :priority)');
118 | $handle->bindParam(':body', $string, PDO::PARAM_STR);
119 | $priority = 45;
120 | $handle->bindParam(':priority', $priority, PDO::PARAM_INT); // intを求めていることをpdoに対して明示する
121 | $handle->execute();
122 |
123 | // 今格納したばかりの文字列を取り出して、きちんと格納できているかどうかを確かめる
124 | $handle = $link->prepare('select * from ElvishSentences where Id = :id');
125 | $id = 7;
126 | $handle->bindParam(':id', $id, PDO::PARAM_INT);
127 | $handle->execute();
128 |
129 | // 結果をオブジェクトに代入して、後でHTMLの中で使う
130 | // このオブジェクトがメモリを圧迫することはない。必要になったその時点ではじめてフェッチする
131 | $result = $handle->fetchAll(\PDO::FETCH_OBJ);
132 |
133 | // ラッパーのサンプル。これはデータをhtml出力用にエスケープする
134 | function escape_to_html($dirty){
135 | echo htmlspecialchars($dirty, ENT_QUOTES, 'UTF-8');
136 | }
137 |
138 | header('Content-Type: text/html; charset=UTF-8'); // すでに default_charset が utf-8 になっているのであればこれは不要
139 | ?>
140 |
141 |
142 |
143 | UTF-8 テストページ
144 |
145 |
146 | Body); // 変換したUTF-8文字列が、ブラウザに正しく出力されるはず
149 | }
150 | ?>
151 |
152 |
153 | {% endhighlight %}
154 |
155 | ### あわせて読みたい
156 |
157 | * [PHPマニュアル: 文字列演算子](https://www.php.net/language.operators.string)
158 | * [PHPマニュアル: String関数](https://www.php.net/ref.strings)
159 | * [`strpos()`](https://www.php.net/function.strpos)
160 | * [`strlen()`](https://www.php.net/function.strlen)
161 | * [`substr()`](https://www.php.net/function.substr)
162 | * [PHPマニュアル: マルチバイト文字列関数](https://www.php.net/ref.mbstring)
163 | * [`mb_strpos()`](https://www.php.net/function.mb-strpos)
164 | * [`mb_strlen()`](https://www.php.net/function.mb-strlen)
165 | * [`mb_substr()`](https://www.php.net/function.mb-substr)
166 | * [`mb_internal_encoding()`](https://www.php.net/function.mb-internal-encoding)
167 | * [`mb_http_output()`](https://www.php.net/function.mb-http-output)
168 | * [`htmlentities()`](https://www.php.net/function.htmlentities)
169 | * [`htmlspecialchars()`](https://www.php.net/function.htmlspecialchars)
170 | * [Stack Overflow: What factors make PHP Unicode-incompatible?](https://stackoverflow.com/questions/571694/what-factors-make-php-unicode-incompatible)
171 | * [Stack Overflow: Best practices in PHP and MySQL with international strings](https://stackoverflow.com/questions/140728/best-practices-in-php-and-mysql-with-international-strings)
172 | * [How to support full Unicode in MySQL databases](https://mathiasbynens.be/notes/mysql-utf8mb4)
173 | * [Bringing Unicode to PHP with Portable UTF-8](https://www.sitepoint.com/bringing-unicode-to-php-with-portable-utf8/)
174 | * [Stack Overflow: DOMDocument loadHTML does not encode UTF-8 correctly](https://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly)
175 |
--------------------------------------------------------------------------------
/_posts/06-01-01-Dependency-Injection.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 依存性の注入
3 | anchor: dependency_injection
4 | ---
5 |
6 | # 依存性の注入 {#dependency_injection_title}
7 |
8 | [Wikipedia](https://wikipedia.org/wiki/Dependency_injection)によると、
9 |
10 | > 依存性の注入とはソフトウェアデザインパターンの1つである。依存関係をソースコードから排除して、実行時あるいはコンパイル時にその依存関係を変更できるようにする。
11 |
12 | とのことだが、この説明は必要以上に小難しく言っているように思える。
13 | 依存性の注入とはコンポーネントに依存関係を渡せる仕組みのことで、コンストラクタで注入したりメソッド呼び出しをしたりプロパティを設定したりといった方法がある。
14 | それだけのことだ。
15 |
--------------------------------------------------------------------------------
/_posts/06-02-01-Basic-Concept.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 基本的な概念
3 | isChild: true
4 | anchor: basic_concept
5 | ---
6 |
7 | ## 基本的な概念 {#basic_concept_title}
8 |
9 | 考えかたを説明するために、シンプルな例を示そう。細かいところは手を抜いているけどね。
10 |
11 | ここに `Database` クラスがある。こいつがデータベースとやりとりするためには、何らかのアダプターが必要だ。
12 | そこで、コンストラクタの中でアダプターのインスタンスを作っている。アダプターの名前を直接指定してね。
13 | こんな書きかただと `Database` クラスを単体でテストするのが難しくなるし、
14 | このクラスがアダプターと密接に結びついてしまう。
15 |
16 | {% highlight php %}
17 | adapter = new MySqlAdapter;
27 | }
28 | }
29 |
30 | class MysqlAdapter {}
31 | {% endhighlight %}
32 |
33 | このコードを書き換えて依存性の注入を使うようにすれば、この依存関係を緩やかにできる。
34 | ここでは、コンストラクタに依存性を注入することにし、クラスを横断してプロパティを使えるようにするために [コンストラクタのプロモーション][php-constructor-promotion] を使うことにしよう:
35 |
36 | {% highlight php %}
37 | query("SELECT some_field FROM some_table");
18 | $row = $statement->fetch(PDO::FETCH_ASSOC);
19 | echo htmlentities($row['some_field']);
20 |
21 | // PDO + SQLite
22 | $pdo = new PDO('sqlite:/path/db/foo.sqlite');
23 | $statement = $pdo->query("SELECT some_field FROM some_table");
24 | $row = $statement->fetch(PDO::FETCH_ASSOC);
25 | echo htmlentities($row['some_field']);
26 | {% endhighlight %}
27 |
28 | PDO は、SQL のクエリをデータベースにあわせて変換するものではないし、
29 | もともと存在しない機能をエミュレートするものでもない。
30 | 純粋に、いろんなデータベースに同じ API で接続するためのものだ。
31 |
32 | もっと大切なことは、`PDO` を使えば、外部からの入力 (ID など)
33 | を安全に SQL クエリに埋め込めるということだ。データベースへの
34 | SQL インジェクション攻撃を心配しなくてもよくなる。
35 | そのためには、PDO ステートメントとバインド変数を使えばよい。
36 |
37 | 数値の ID をクエリ文字列として受け取る PHP スクリプトを考えてみよう。
38 | 渡された ID を使って、データベースからユーザー情報を取り出す。
39 | 最初に示すのは `悪い方法` だ。
40 |
41 | {% highlight php %}
42 | query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- ダメ、ゼッタイ!
45 | {% endhighlight %}
46 |
47 | こんな恐ろしいコードを書いちゃいけない。
48 | これは、クエリ文字列のパラメータを SQL に直に埋め込んでいることになる。
49 | あっという間に [SQLインジェクション] 攻撃を食らうだろう。
50 | どこかのずるがしこい攻撃者が `id` に渡す内容をひと工夫して
51 | `http://domain.com/?id=1%3BDELETE+FROM+users` みたいな URL を呼んだとしよう。
52 | このとき変数 `$_GET['id']` の内容は `1;DELETE FROM users` となり、全ユーザーが消えてしまうことになる!
53 | こんな書き方ではなく、PDO のバインド変数で ID を受け取らないといけない。
54 |
55 | {% highlight php %}
56 | prepare('SELECT name FROM users WHERE id = :id');
59 | $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- まずデータのフィルタリングを行う ([データのフィルタリング](#data_filtering) を参照)。特に INSERT や UPDATE などで重要
60 | $stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- PDOが自動的にSQLのエスケープ処理をする
61 | $stmt->execute();
62 | {% endhighlight %}
63 |
64 | これが正しい方法だ。この例では、PDO ステートメントでバインド変数を使っている。
65 | 外部からの入力である ID がエスケープされてからデータベースに渡るので、
66 | SQL インジェクション攻撃を受けることがなくなる。
67 |
68 | INSERT や UPDATE などの書き込み操作の場合は、まず [データをフィルタリング](#data_filtering) することが大切だ。
69 | その後で、その他 (HTML タグや JavaScript など) のエスケープを行う。
70 | PDO はあくまでも SQL 用のエスケープを行うものであり、アプリケーション全体の面倒をみてくれるわけではない。
71 |
72 | * [PDOについて調べる][pdo]
73 |
74 | あと、データベースのコネクションはリソースを使うということにも気をつけよう。
75 | コネクションを明示的に閉じることを忘れたせいでリソースを食いつぶしてしまうなんて話は珍しくない。
76 | とはいえ、これは別にPHPに限った話でもないけどね。
77 | PDOを使っている場合は、オブジェクトへの参照をすべて削除して(Nullを代入するなどして)
78 | オブジェクトを破棄してしまえば、暗黙のうちにコネクションを閉じることが保証される。
79 | オブジェクトを明示的に破棄しない場合は、スクリプトの実行が終わった時点でPHPが自動的に接続を閉じる。
80 | もちろん、持続的接続を使っている場合は別だ。
81 |
82 | * [PDOの接続について調べる]
83 |
84 |
85 | [pdo]: https://www.php.net/pdo
86 | [SQLインジェクション]: https://web.archive.org/web/20210413233627/http://wiki.hashphp.org/Validation
87 | [PDOの接続について調べる]: https://www.php.net/pdo.connections
88 |
--------------------------------------------------------------------------------
/_posts/07-04-01-Interacting-via-Code.md:
--------------------------------------------------------------------------------
1 | ---
2 | isChild: true
3 | title: データベースとのやりとり
4 | anchor: databases_interacting
5 | ---
6 |
7 | ## データベースとのやりとり {#databases_interacting_title}
8 |
9 | PHPを勉強し始めたばかりの開発者がやってしまいがちなのが、データベースとのやりとりと画面表示のロジックをごちゃまぜにしてしまうことだ。
10 | たとえば、こんなコードになる。
11 |
12 | {% highlight php %}
13 |
14 | query('SELECT * FROM table') as $row) {
16 | echo "- ".$row['field1']." - ".$row['field1']."
";
17 | }
18 | ?>
19 |
20 | {% endhighlight %}
21 |
22 | これは、あらゆる意味でよろしくない。
23 | まず何と言っても、デバッグしづらいし、テストもしづらいし、読みづらい。
24 | あと、何も制限をかけていない場合に、大量のフィールドを出力してしまうことになる。
25 |
26 | 同じことをもっとすっきり行う方法はいろいろある。[OOP](/#object-oriented-programming)が好きな人向けのやりかたもあれば
27 | [関数型プログラミング](/#functional-programming)が好きな人向けのやりかたもある。
28 | が、まずは、分離することからはじめよう。
29 |
30 | これが第一歩だ。
31 |
32 | {% highlight php %}
33 | query('SELECT * FROM table');
36 | }
37 |
38 | $results = getAllFoos($db);
39 | foreach ($results as $row) {
40 | echo "".$row['field1']." - ".$row['field1'].""; // BAD!!
41 | }
42 | {% endhighlight %}
43 |
44 | 少しはマシになった。この二つを別々のファイルに分けてしまえば、きれいに分離できるだろう。
45 |
46 | 次に、このメソッドを保持するクラスを用意する。「モデル」だ。
47 | そして、シンプルな `.php` ファイルをもうひとつ作って、そこに画面表示ロジックを入れる。「ビュー」だ。
48 | これで、何となく [MVC] っぽくなった。これは、多くの [フレームワーク](/#frameworks) で使われている、OOPのアーキテクチャだ。
49 |
50 | **foo.php**
51 |
52 | {% highlight php %}
53 | getAllFoos();
63 |
64 | // ビューを表示する
65 | include 'views/foo-list.php';
66 | {% endhighlight %}
67 |
68 |
69 | **models/FooModel.php**
70 |
71 | {% highlight php %}
72 | db->query('SELECT * FROM table');
81 | }
82 | }
83 | {% endhighlight %}
84 |
85 | **views/foo-list.php**
86 |
87 | {% highlight php %}
88 |
89 | = $row['field1'] ?> - = $row['field1'] ?>
90 |
91 | {% endhighlight %}
92 |
93 | 本質的にこれは、今どきのフレームワークがやっていることと同じだ。それを手作業でやってみた。
94 | 毎回こんなことをする必要はないかもしれないが、画面表示とデータベース操作を混在させすぎると、
95 | [ユニットテスト](/#unit-testing) をしたくなったときにやっかいな問題が発生してしまう。
96 |
97 | [MVC]: https://code.tutsplus.com/tutorials/mvc-for-noobs--net-10488
98 |
--------------------------------------------------------------------------------
/_posts/07-05-01-Abstraction-Layers.md:
--------------------------------------------------------------------------------
1 | ---
2 | isChild: true
3 | title: 抽象化レイヤー
4 | anchor: databases_abstraction_layers
5 | ---
6 |
7 | ## 抽象化レイヤー {#databases_abstraction_layers_title}
8 |
9 | 多くのフレームワークは、データベースの抽象化レイヤーを用意している。[PDO][1]を利用しているものもあれば、そうでないものもある。
10 | あるデータベースには存在するけれども別のデータベースには存在しない機能などをエミュレートするために、
11 | クエリーをPHPのメソッドでラップしたりするものだ。
12 | PDOではデータベースへの接続の抽象化しかしないが、フレームワークの抽象化レイヤーは、それ以上のことをしてくれる。
13 | もちろんそれなりのオーバーヘッドが発生するだろう。
14 | でも、MySQLでもPostgreSQLでもSQLiteでも動くようなアプリケーションを書いているのなら、
15 | 多少のオーバーヘッドを割り引いてでも、すっきりしたコードを書けるほうがうれしいだろう。
16 |
17 | 以下の抽象化レイヤーは、 [PSR-0][psr0] や [PSR-4][psr4] で定められた標準名前空間に従っている。
18 | これらはアプリケーションを問わずに利用できる。
19 |
20 | * [Atlas][5]
21 | * [Aura SQL][6]
22 | * [Doctrine2 DBAL][2]
23 | * [Medoo][8]
24 | * [Propel][7]
25 | * [laminas-db][4]
26 |
27 |
28 | [1]: https://www.php.net/book.pdo
29 | [2]: https://www.doctrine-project.org/projects/dbal.html
30 | [4]: https://docs.laminas.dev/laminas-db/
31 | [5]: https://atlasphp.io
32 | [6]: https://github.com/auraphp/Aura.Sql
33 | [7]: https://propelorm.org/
34 | [8]: https://medoo.in/
35 | [psr0]: https://www.php-fig.org/psr/psr-0/
36 | [psr4]: https://www.php-fig.org/psr/psr-4/
37 |
--------------------------------------------------------------------------------
/_posts/08-01-01-Templating.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: テンプレート
3 | anchor: templating
4 | ---
5 |
6 | # テンプレート {#templating_title}
7 |
8 | テンプレートは、コントローラーやドメインロジックを、プレゼンテーションロジックから切り離すための便利な手段だ。
9 | テンプレ—トには、アプリケーションで使うHTMLを含めることが多いが、それ以外のフォーマット(XMLなど)を含めることもある。
10 | テンプレートのことを「ビュー」と呼ぶこともある。いわゆる
11 | [model–view–controller](/pages/Design-Patterns.html#model-view-controller) (MVC)
12 | パターンの、二番目の要素 **の一部** だ。
13 |
--------------------------------------------------------------------------------
/_posts/08-02-01-Benefits.md:
--------------------------------------------------------------------------------
1 | ---
2 | isChild: true
3 | title: メリット
4 | anchor: templating_benefits
5 | ---
6 |
7 | ## メリット {#templating_benefits_title}
8 |
9 | テンプレートを使う主なメリットは、画面に表示する内容をアプリケーションから切り離せることだ。
10 | テンプレートは、フォーマット済みのコンテンツを表示するという責務だけを負う。
11 | データを検索したり、データベースに保存したりなどといった、雑多なタスクは気にしない。
12 | このおかげで、すっきりしたリーダブルなコードが書けるようになる。チームで開発する際などには、これが特に有用だ。
13 | 開発者はサーバー側のコード(コントローラやモデル)、そしてデザイナーはクライアント側のコード(マークアップ)
14 | などという作業分担をしやすくなる。
15 |
16 | テンプレートには、プレゼンテーションのコードの構造を改善するという効果もある。
17 | テンプレートは一般的に「views」などのフォルダにまとめられて、それぞれ個別のファイルになっていることが多い。
18 | こうしておけばコードの再利用がしやすくなる。大規模なコードブロックを、小さめの再利用しやすいパーツ(パーシャルと呼ばれることもある)に分割できるからだ。
19 | たとえば、サイトのヘッダやフッタをテンプレートにしておけば、各ページのテンプレートにそれをインクルードできるようになる。
20 |
21 | 利用するライブラリによっては、テンプレートを使うことで、よりセキュリティを確保できるようにもなる。
22 | ユーザーが作るコンテンツを自動的にエスケープしてくれたりする機能を持つようなテンプレートが、それにあたる。
23 | さらに、サンドボックス機能を提供するライブラリもある。これは、デザイナーが、あらかじめ許可された変数と関数しか利用できないようにする仕組みだ。
24 |
--------------------------------------------------------------------------------
/_posts/08-03-01-Plain-PHP-Templates.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Plain PHP Templates
3 | isChild: true
4 | title: プレーンなPHPによるテンプレート
5 | anchor: plain_php_templates
6 | ---
7 |
8 | ## プレーンなPHPによるテンプレート {#plain_php_templates_title}
9 |
10 | プレーンなPHPによるテンプレートとは、単にPHPのコードを使ったテンプレートという意味だ。
11 | ごく自然な選択肢だとも言える。そもそもPHP自体がテンプレート言語だし。
12 | あ、これって単に、PHPのコードをHTMLとかにも埋め込めるよねっていう以上の深い意味はないからね。
13 | PHPの開発者にとっては、新しい構文を覚えずに済むというメリットがある。
14 | どんな関数が使えるのかもわかっているし、ふだんPHPを書いているエディタのシンタックスハイライトや
15 | 自動補完機能も、そのまま使える。
16 | その上、プレーンなPHPのテンプレートは高速であることが多い。コンパイルが不要だからだ。
17 |
18 | いまどきのPHPフレームワークは、たいてい何らかの仕組みのテンプレートシステムを持っている。
19 | その多くでデフォルトになっているのが、プレーンPHPによるテンプレートだ。
20 | フレームワーク以外では、[Plates][plates]や[Aura.View][aura]
21 | といったライブラリがプレーンPHPテンプレートを使いやすくしてくれる。継承やレイアウト、拡張などの便利なテンプレート機能を用意してくれるんだ。
22 |
23 | ### プレーンPHPテンプレートのシンプルな例
24 |
25 | [Plates][plates] ライブラリを使った。
26 |
27 | {% highlight php %}
28 |
29 |
30 | insert('header', ['title' => 'User Profile']) ?>
31 |
32 | User Profile
33 | Hello, =$this->escape($name)?>
34 |
35 | insert('footer') ?>
36 | {% endhighlight %}
37 |
38 | ### プレーンPHPテンプレートで継承を使う例
39 |
40 | [Plates][plates] ライブラリを使った。
41 |
42 | {% highlight php %}
43 |
44 |
45 |
46 |
47 | =$title?>
48 |
49 |
50 |
51 |
52 | =$this->section('content')?>
53 |
54 |
55 |
56 |
57 | {% endhighlight %}
58 |
59 | {% highlight php %}
60 |
61 |
62 | layout('template', ['title' => 'User Profile']) ?>
63 |
64 | User Profile
65 | Hello, =$this->escape($name)?>
66 | {% endhighlight %}
67 |
68 |
69 | [plates]: https://platesphp.com/
70 | [aura]: https://github.com/auraphp/Aura.View
71 |
--------------------------------------------------------------------------------
/_posts/08-04-01-Compiled-Templates.md:
--------------------------------------------------------------------------------
1 | ---
2 | isChild: true
3 | title: コンパイル形式のテンプレート
4 | anchor: compiled_templates
5 | ---
6 |
7 | ## コンパイル形式のテンプレート {#compiled_templates_title}
8 |
9 | PHPはオブジェクト指向言語として成熟してきてはいるものの、テンプレート言語としては
10 | [いまいち][article_templating_engines] だ。
11 | コンパイル形式のテンプレート、たとえば [Twig] や [Brainy] や [Smarty]*
12 | が、この穴を埋めてくれる。テンプレートに特化した、新しい構文を用意してくれるんだ。
13 | 自動エスケープから継承や制御構文まで、コンパイル形式のテンプレートは、いかに読み書きしやすく、安心して使えるかを重視して作られている。
14 | さらに、コンパイル形式のテンプレートは、別の言語でさえも使うことができる。[Mustache] がそのよい例だ。
15 | テンプレートをコンパイルする時間がかかるので、多少はパフォーマンスに影響する。
16 | しかし、適切にキャッシュをすれば、その影響は微々たるものだ。
17 |
18 | **Smartyには自動エスケープ機能があるけど、これはデフォルトでは無効になっている。*
19 |
20 | ### コンパイル形式のテンプレートのシンプルな例
21 |
22 | [Twig] ライブラリを使った。
23 |
24 | {% highlight html+jinja %}
25 | {% raw %}
26 | {% include 'header.html' with {'title': 'User Profile'} %}
27 |
28 | User Profile
29 | Hello, {{ name }}
30 |
31 | {% include 'footer.html' %}
32 | {% endraw %}
33 | {% endhighlight %}
34 |
35 | ### コンパイル形式のテンプレートで継承を使う例
36 |
37 | [Twig] ライブラリを使った。
38 |
39 | {% highlight html+jinja %}
40 | {% raw %}
41 | // template.html
42 |
43 |
44 |
45 | {% block title %}{% endblock %}
46 |
47 |
48 |
49 |
50 | {% block content %}{% endblock %}
51 |
52 |
53 |
54 |
55 | {% endraw %}
56 | {% endhighlight %}
57 |
58 | {% highlight html+jinja %}
59 | {% raw %}
60 | // user_profile.html
61 |
62 | {% extends "template.html" %}
63 |
64 | {% block title %}User Profile{% endblock %}
65 | {% block content %}
66 | User Profile
67 | Hello, {{ name }}
68 | {% endblock %}
69 | {% endraw %}
70 | {% endhighlight %}
71 |
72 |
73 | [article_templating_engines]: http://fabien.potencier.org/templating-engines-in-php.html
74 | [Twig]: https://twig.symfony.com/
75 | [Brainy]: https://github.com/box/brainy
76 | [Smarty]: https://www.smarty.net/
77 | [Mustache]: https://mustache.github.io/
78 |
--------------------------------------------------------------------------------
/_posts/08-05-01-Further-Reading.md:
--------------------------------------------------------------------------------
1 | ---
2 | isChild: true
3 | title: あわせて読みたい
4 | anchor: templating_further_reading
5 | ---
6 |
7 | ## あわせて読みたい {#templating_further_reading_title}
8 |
9 | ### 記事やチュートリアル
10 |
11 | * [Templating Engines in PHP](http://fabien.potencier.org/templating-engines-in-php.html)
12 | * [An Introduction to Views & Templating in CodeIgniter](https://code.tutsplus.com/tutorials/an-introduction-to-views-templating-in-codeigniter--net-25648)
13 | * [Getting Started With PHP Templating](https://www.smashingmagazine.com/2011/10/getting-started-with-php-templating/)
14 | * [Roll Your Own Templating System in PHP](https://code.tutsplus.com/tutorials/roll-your-own-templating-system-in-php--net-16596)
15 | * [Master Pages](https://laracasts.com/series/laravel-from-scratch/episodes/7)
16 | * [Working With Templates in Symfony 2](https://code.tutsplus.com/tutorials/working-with-templates-in-symfony-2--cms-21172)
17 | * [Writing Safer Templates](https://github.com/box/brainy/wiki/Writing-Safe-Templates)
18 |
19 | ### ライブラリ
20 |
21 | * [Aura.View](https://github.com/auraphp/Aura.View) *(native)*
22 | * [Blade](https://laravel.com/docs/blade) *(compiled, framework specific)*
23 | * [Brainy](https://github.com/box/brainy) *(compiled)*
24 | * [Latte](https://github.com/nette/latte) *(compiled)*
25 | * [Mustache](https://github.com/bobthecow/mustache.php) *(compiled)*
26 | * [PHPTAL](https://phptal.org/) *(compiled)*
27 | * [Plates](https://platesphp.com/) *(native)*
28 | * [Smarty](https://www.smarty.net/) *(compiled)*
29 | * [Twig](https://twig.symfony.com/) *(compiled)*
30 | * [laminas-view](https://docs.laminas.dev/laminas-view/) *(native, framework specific)*
31 |
--------------------------------------------------------------------------------
/_posts/09-01-01-Errors-and-Exceptions.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: エラーと例外処理
3 | anchor: errors_and_exceptions
4 | ---
5 |
6 | # エラーと例外処理 {#errors_and_exceptions_title}
7 |
8 |
--------------------------------------------------------------------------------
/_posts/09-02-01-Errors.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: エラー
3 | isChild: true
4 | anchor: errors
5 | ---
6 |
7 | ## エラー {#errors_title}
8 |
9 | 例外処理を重視するプログラミング言語では、何か問題が起こったらすぐに例外を投げる。
10 | それはそれでいいことではあるが、PHPはそうではなく、「例外処理も使える」プログラミング言語だ。
11 | PHPには例外処理の仕組みがあるし、コアの中でもオブジェクトを扱うときには例外処理を行うことが増えている。
12 | でも、PHPは基本的に、よっぽど致命的なエラーが発生しない限りは何があろうと処理を続行しようとする。
13 |
14 | たとえば、こんなコードを考えてみよう。
15 |
16 | {% highlight console %}
17 | $ php -a
18 | php > echo $foo;
19 | Notice: Undefined variable: foo in php shell code on line 1
20 | {% endhighlight %}
21 |
22 | 単純にnoticeレベルのエラーになるだけで、PHPはそのまま処理を続行する。
23 | 例外処理を重視する世界からやってきた人にとっては、これは少しキモいと思うかもしれないね。
24 | たとえばPythonなら、未定義の変数を参照しようとすると、例外が発生する。
25 |
26 | {% highlight console %}
27 | $ python
28 | >>> print foo
29 | Traceback (most recent call last):
30 | File "", line 1, in
31 | NameError: name 'foo' is not defined
32 | {% endhighlight %}
33 |
34 | 実際の違いは、こういうことだ。
35 | Pythonは、些細なことにまでこだわることで、プログラマーが余計な心配(「もし○○だったら、その場合は…」と考えたり、エッジケースを考慮したりなど)
36 | をせずに済むようにしている。
37 | 一方PHPは、どうしようもないエラーが発生しないかぎりは、一応エラーを報告したうえで処理を続行する。
38 |
39 | ### エラーの深刻度
40 |
41 | PHPのエラーは、何段階かの深刻度レベルに別れている。PHPでよく使われるメッセージの形式は、
42 | エラー(error)と注意(notice)そして警告(warning)だ。
43 | それぞれ別々の深刻度レベルが設定されていて、 `E_ERROR`、`E_NOTICE`、そして `E_WARNING` になる。
44 | 「エラー」は実行時の致命的な問題で、ふつうはコードの書きかたがまずいせいで発生する。
45 | これは修正しなければいけない。というのも、これが発生するとPHPの実行がそこで止まってしまうからだ。
46 | 「注意」は助言みたいなもので、問題を起こす可能性があるスクリプトを実行したときに発生する。
47 | スクリプトの実行は止まらない。
48 | 「警告」は致命的ではない問題で、これもスクリプトの実行は止まらない。
49 |
50 | もうひとつ、コンパイル時に発生する `E_STRICT` という形式のメッセージもある。
51 | これは、相互運用性や将来のバージョンのPHPとの互換性を考えたときに、コードを書き換えたほうがいいと提案するためのメッセージだ。
52 |
53 | ### PHPのエラー報告の挙動の変更
54 |
55 | エラー報告の挙動は、PHPの設定で変更することもできるしPHPの関数で変更することもできる。
56 | 組み込みの関数 `error_reporting()` にエラーレベル定数を渡せば、そのスクリプトの実行中に
57 | どのレベルのエラー処理をするのかを設定できる。
58 | たとえば、エラー(error)とか警告(warning)は表示させたいけれども、別に注意(notice)は見たくないという場合は、こんなふうにすればいい。
59 |
60 | {% highlight php %}
61 | upload->get_error()` みたいなメソッドを実行することになる。
19 | 何が問題かというと、まずエラーが発生したのかどうかを自分で調べないといけないこと。
20 | そして次に、エラー情報を取得する方法をドキュメントで調べないといけないこと。
21 | もっとはっきりわかるようにしてくれたらいいのに。
22 |
23 | 別の問題もある。何かのクラスがエラーを画面に投げっぱなしにしてそのまま終わるような場合だ。
24 | そんなことをすれば、エラーがあったときにプログラム中で動的に対応することができなくなってしまう。
25 | そんな場合は、エラーではなく例外を発生させないといけない。
26 | 例外にしておけば開発者がエラーに気づけるし、プログラムの中で対応できるようになる。
27 | たとえばこんな感じだ。
28 |
29 | {% highlight php %}
30 | subject('タイトル');
33 | $email->body('ごきげんいかが?');
34 | $email->to('guy@example.com', '誰かさん');
35 |
36 | try
37 | {
38 | $email->send();
39 | }
40 | catch(Fuel\Email\ValidationFailedException $e)
41 | {
42 | // 検証に失敗した
43 | }
44 | catch(Fuel\Email\SendingFailedException $e)
45 | {
46 | // ドライバがメールを送れなかった
47 | }
48 | finally
49 | {
50 | // 例外が発生してもしなくても、ここは必ず実行される
51 | }
52 | {% endhighlight %}
53 |
54 | ### SPL の例外
55 |
56 | 汎用的な `Exception` クラスには、開発者がデバッグするためのコンテキスト情報がほとんど含まれていない。
57 | これを改善するには、特化型の `Exception` を作ればいい。つまり、`Exception` クラスのサブクラスを作るってことだ。
58 |
59 | {% highlight php %}
60 | 大量に できあがってしまうかもしれないが、
66 | [SPL 拡張モジュール][splext] が用意する例外クラスを使えば少しはましになるだろう。
67 |
68 | たとえば、マジックメソッド `__call()` を使っているときに、無効なメソッドを要求されたとしよう。
69 | 標準の Exception クラスを使うのは曖昧すぎるし、そのためだけに専用の例外クラスを作るのも何だし、
70 | という場合には単に `throw new BadMethodCallException;` とすればよい。
71 |
72 | * [例外について][exceptions]
73 | * [SPL 拡張モジュール][splexe]
74 | * [PHP での例外のネスト][nesting-exceptions-in-php]
75 |
76 |
77 | [splext]: /#standard_php_library
78 | [exceptions]: https://www.php.net/language.exceptions
79 | [splexe]: https://www.php.net/spl.exceptions
80 | [nesting-exceptions-in-php]: https://www.brandonsavage.net/exceptional-php-nesting-exceptions-in-php/
81 |
--------------------------------------------------------------------------------
/_posts/10-01-01-Security.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: セキュリティ
3 | anchor: security
4 | ---
5 |
6 | # セキュリティ {#security_title}
7 |
8 | 今まで見たPHPのセキュリティに関する資料の中でいちばんだったのは、[Paragon Initiative](https://paragonie.com/) による
9 | [The 2018 Guide to Building Secure PHP Software](https://paragonie.com/blog/2017/12/2018-guide-building-secure-php-software)だ。
10 |
--------------------------------------------------------------------------------
/_posts/10-02-01-Web-Application-Security.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ウェブアプリケーションのセキュリティ
3 | isChild: true
4 | anchor: web_application_security
5 | ---
6 |
7 | ## ウェブアプリケーションのセキュリティ {#web_application_security_title}
8 |
9 | PHP開発者は全員、[Webアプリケーションセキュリティの基本][4] を学ぶべきだ。内容としてはだいたいこんな感じになる。
10 | down into a handful of broad topics:
11 |
12 | 1. コードとデータの分離
13 | * データをコードとして実行するときにはSQLインジェクションやクロスサイトスクリプティングやファイルインクルード攻撃の恐れがある
14 | * コードをデータとして表示するときには情報漏洩の恐れがある (ソースコードが晒されてしまったり、C言語の場合ならアドレス空間配置のランダム化もある [ASLR][5])
15 | 2. アプリケーションロジック
16 | * 認証・認可の制御不全
17 | * 入力の検証
18 | 3. 運用環境
19 | * PHPのバージョン
20 | * サードパーティのライブラリ
21 | * OS
22 | 4. 暗号化の弱点
23 | * [弱い乱数][6].
24 | * [選択暗号文攻撃][7].
25 | * [サイドチャネル情報漏洩][8].
26 |
27 | 世の中には悪い人たちがいて、あなたの書いたウェブアプリケーションもきっと狙われている。
28 | 必要な対策をして、ウェブアプリケーションのセキュリティを固めておくことが大切だ。
29 | ありがたいことに、[The Open Web Application Security Project][1] (OWASP)
30 | の人たちが、既知のセキュリティ問題とその対策をまとめてくれている。
31 | セキュリティが気になる開発者は必読だ。
32 | Padraic Bradyが書いた[Survive The Deep End: PHP Security][3] も、
33 | ウェブアプリケーションセキュリティに関してPHP向けに書かれたよいドキュメントだ。
34 |
35 | * [OWASP Security Guideを読む][2]
36 |
37 |
38 | [1]: https://www.owasp.org/
39 | [2]: https://www.owasp.org/index.php/Guide_Table_of_Contents
40 | [3]: https://phpsecurity.readthedocs.io/en/latest/index.html
41 | [4]: https://paragonie.com/blog/2015/08/gentle-introduction-application-security
42 | [5]: https://www.techtarget.com/searchsecurity/definition/address-space-layout-randomization-ASLR
43 | [6]: https://paragonie.com/blog/2016/01/on-design-and-implementation-stealth-backdoor-for-web-applications
44 | [7]: https://paragonie.com/blog/2015/05/using-encryption-and-authentication-correctly
45 | [8]: https://blog.ircmaxell.com/2014/11/its-all-about-time.html
46 |
--------------------------------------------------------------------------------
/_posts/10-03-01-Password-Hashing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: パスワードのハッシュ処理
3 | isChild: true
4 | anchor: password_hashing
5 | ---
6 |
7 | ## パスワードのハッシュ処理 {#password_hashing_title}
8 |
9 | 誰もがいつかは、ログイン機能を持つ PHP アプリケーションを書くことになる。
10 | ユーザー名とパスワード (のハッシュ) をデータベースに保存して、
11 | ユーザーのログイン時にそれを使って認証するというやつだ。
12 |
13 | データベースにパスワードを保存するときは、適切に [_ハッシュ_][3] することが大切だ。
14 | ハッシュと暗号化は [まったく違うもの][7] なのに、混同されることが多い。
15 |
16 | パスワードのハッシュは不可逆な操作で、ユーザーのパスワードに対して一方通行で行う。
17 | できあがる結果は固定長の文字列で、元には戻せない。
18 | つまり、このハッシュを別のハッシュと比較すれば元の文字列どうしが一致するかどうかは判断できるが、
19 | 元の文字列が何だったかはわからないってことだ。
20 | パスワードをハッシュせずにデータベースに保存していると、
21 | 万一第三者に不正アクセスされた場合に、すべてのユーザーアカウントが乗っ取られてしまう。
22 |
23 | ハッシュと違って暗号化は元に戻せる (鍵さえ手元にあればね)。
24 | 暗号化そのものは使う場面を選べば便利なものだけど、ことパスワードの保存に関してはうまい手段ではない。
25 |
26 | パスワードには個別に [_ソルト_][5] が必要だ。ランダムな文字列をパスワードに付けてからハッシュするってこと。
27 | そうしておけば、辞書攻撃から守れるし「レインボーテーブル(ありがちなパスワードとそのハッシュをまとめた変換テーブル)」による攻撃も防げる。
28 |
29 | ハッシュとソルトは欠かせない。だって、たいていのユーザーはいろんなサービスでパスワードを使い回すものだし、
30 | パスワード自体も決して強力なものだとは言えないから。
31 |
32 | あと、速度が売りの汎用ハッシュ関数 (SHA256とか) じゃなくて [_パスワードのハッシュ_ に特化したアルゴリズム][6] を使うこと。
33 | 2018年6月時点でパスワードのハッシュに使ってもかまわないアルゴリズムはこんな感じ。
34 |
35 | * Argon2 (PHP 7.2 以降で使える)
36 | * Scrypt
37 | * **Bcrypt** (PHP が用意してくれている。詳細は後ほど)
38 | * PBKDF2 と HMAC-SHA256 あるいは HMAC-SHA512 の組み合わせ
39 |
40 | ありがたいことに、最近の PHP ならこのあたりも使いやすい。
41 |
42 | **`password_hash`によるパスワードのハッシュ**
43 |
44 | PHP 5.5からは、新たに`password_hash()`関数が使えるようになった。
45 | 現時点では、この関数はBCryptを使っている。これは、現在のPHPがサポートしているアルゴリズムの中では最強のものだ。
46 | 必要に応じて、将来はもっと強力なアルゴリズムをサポートするように更新されるだろう。
47 | この関数をPHP 5.5より前のバージョンでも使えるようにするため、`password_compat`ライブラリも作られた。
48 | このライブラリはPHP 5.3.7以降で使える。
49 |
50 | この例では、文字列をハッシュした後でそのハッシュを新たな文字列と比較している。
51 | 二つの文字列は違っている('secret-password'と'bad-password')ので、このログインは失敗する。
52 |
53 | {% highlight php %}
54 | = 5.3.7 && < 5.5 で使える `password_compat`] [2]
70 | * [暗号学的なハッシュについて調べる] [3]
71 | * [ソルトについて調べる] [5]
72 | * [PHP `password_hash()` RFC] [4]
73 |
74 |
75 | [1]: https://www.php.net/function.password-hash
76 | [2]: https://github.com/ircmaxell/password_compat
77 | [3]: http://ja.wikipedia.org/wiki/暗号学的ハッシュ関数
78 | [4]: https://wiki.php.net/rfc/password_hash
79 | [5]: https://wikipedia.org/wiki/Salt_(cryptography)
80 | [6]: https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016
81 | [7]: https://paragonie.com/blog/2015/08/you-wouldnt-base64-a-password-cryptography-decoded
82 |
83 |
--------------------------------------------------------------------------------
/_posts/10-04-01-Data-Filtering.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: データのフィルタリング
3 | isChild: true
4 | anchor: data_filtering
5 | ---
6 |
7 | ## データのフィルタリング {#data_filtering_title}
8 |
9 | PHP のコードに外部から渡される入力は、絶対に信用してはいけない。
10 | 外部からの入力は、常に検証してから使うようにしよう。
11 | `filter_var()` 関数や `filter_input()` 関数で、入力の検証や書式の判定 (メールアドレスなど)
12 | ができる。
13 |
14 | 外部からの入力にはいろいろな種類がある。フォームから渡される `$_GET` や `$_POST` もあれば、
15 | スーパーグローバル `$_SERVER` に含まれるものもある。そして
16 | `fopen('php://input', 'r')` でやってくる HTTP リクエストのボディもそうだ。
17 | 外部からの入力といっても、ユーザーがフォームで入力したものばかりとは限らないことに注意。
18 | アップロードしたりダウンロードしたりしたファイル、セッションのデータ、
19 | クッキーのデータ、サードパーティのウェブサービスからのデータ。
20 | これらはみんな外部からの入力となる。
21 |
22 | 外部からのデータをいったん保存して、何かと組み合わせて後で使うとしよう。
23 | それでも、そのデータが外部からの入力であるという事実は変わらない。
24 | そのデータを処理したり出力したり何かとつなげたりコードに組み込んだりするときには
25 | 「適切にフィルタリングできてる?」「信頼できる?」と確認しよう。
26 |
27 | データのフィルタリング方法は、その利用目的によって異なる。
28 | たとえば、外部の入力を何も処理せずに HTML ページに出力すると、
29 | あなたのサイト上で任意の JavaScript が実行できてしまうことになる!
30 | これが、いわゆるクロスサイトスクリプティング (XSS) である。
31 | とても危険な攻撃だ。こんなときに XSS を回避する方法のひとつは、
32 | `strip_tags()` で入力からすべての HTML タグを取り除くか、
33 | あるいは `htmlentities()` や `htmlspecialchars()` でエスケープして HTML エンティティに変換することだ。
34 |
35 | 別の例として、外部の入力をコマンドラインのオプションとして渡すことを考えよう。
36 | これって非常に危険なことだし、ふつうはあまりやるべきではないことだ。
37 | でも、もしやるなら、組み込みの関数 `escapeshellarg()`
38 | を使えばコマンドの引数を実行されてしまうことが防げる。
39 |
40 | 最後の例は、外部の入力に基づいてファイルシステム上のファイルを読み込むというものだ。
41 | このときは、ファイル名のかわりにファイルパスを渡されてしまうという攻撃が考えられる。
42 | 外部の入力から"/"や"../"、[null バイト][6]などを取り除いて、
43 | 隠しファイルや公開すべきでない場所のファイルを読み込まないようにする必要がある。
44 |
45 | * [データのフィルタリング][1]
46 | * [`filter_var` 関数][4]
47 | * [`filter_input` 関数][5]
48 | * [null バイトの扱い][6]
49 |
50 | ### サニタイズ
51 |
52 | サニタイズとは、外部の入力から危険な文字を取り除く (あるいはエスケープする) ことだ。
53 |
54 | たとえば、外部の入力を HTML に含めたり SQL クエリに組み込んだりする前に、
55 | サニタイズが必要となる。[PDO](#databases) でバインド変数を使う場合は、
56 | PDO が入力をサニタイズする。
57 |
58 | 外部の入力を HTML として組み込むときに、いくつかの安全な HTML
59 | タグはそのまま使わせたいという場合もある。そんな要求を実現するのは非常に難しいので、
60 | たいていの場合は Markdown や BBCode などのフォーマットを代替手段として使うのだが、
61 | どうしてもという場合は [HTML Purifier][html-purifier]
62 | のようなホワイトリストライブラリを使える。
63 |
64 | [サニタイズフィルター][2]
65 |
66 | ### アンシリアライズ
67 |
68 | ユーザーからの入力など、信頼できないところから渡されたデータを `unserialize()` するのは危険だ。
69 | 悪意のあるユーザーが送り込んだオブジェクト(ユーザー定義のプロパティつきのもの)のインスタンスを生成できてしまい、
70 | **たとえそのオブジェクトを一切使わなくても** そのデストラクタは実行されてしまう。
71 | なので、信頼できないデータのアンシリアライズは避けるべきだ。
72 |
73 | シリアル化したデータをユーザーに渡す必要がある場合は、([`json_decode`][json_decode] や [`json_encode`][json_encode] 経由で) JSON のような安全で標準的なデータ交換フォーマットを使うようにしよう。
74 |
75 | ### バリデーション
76 |
77 | バリデーションとは、外部の入力が期待通りであるかどうかを確かめること。
78 | たとえばユーザー登録の処理では、
79 | メールアドレスや電話番号、あるいは年齢などを検証することになるだろう。
80 |
81 | [バリデーションフィルター][3]
82 |
83 |
84 | [1]: https://www.php.net/book.filter
85 | [2]: https://www.php.net/filter.filters.sanitize
86 | [3]: https://www.php.net/filter.filters.validate
87 | [4]: https://www.php.net/function.filter-var
88 | [5]: https://www.php.net/function.filter-input
89 | [6]: https://www.php.net/security.filesystem.nullbytes
90 | [html-purifier]: http://htmlpurifier.org/
91 | [json_decode]: https://www.php.net/manual/function.json-decode.php
92 | [json_encode]: https://www.php.net/manual/function.json-encode.php
93 |
--------------------------------------------------------------------------------
/_posts/10-05-01-Configuration-Files.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 設定ファイル
3 | isChild: true
4 | anchor: configuration_files
5 | ---
6 |
7 | ## 設定ファイル {#configuration_files_title}
8 |
9 | 自作のアプリケーションで設定ファイルを使うときには、これらの指針に従うのがお勧めだ。
10 |
11 | - 設定情報に直接アクセスしたり、ファイルシステム経由で取り込んだりできないようにする。
12 | - どうしてもドキュメントルートに設定ファイルを置かざるを得ないのなら、そのファイルの拡張子を`.php`にする。
13 | そうすれば、仮にそのファイルへ直接アクセスされたとしても、中身がそのまま見えてしまうことはない。
14 | - 設定ファイル内の情報は適切に保護すること。暗号化するなり、ファイルシステム上のパーミッションをきちんと設定するなりしておく。
15 | - パスワードやAPIトークンなどの機密情報を含んだ設定ファイルは、ソース管理システムに含めないようにする。
16 |
--------------------------------------------------------------------------------
/_posts/10-06-01-Register-Globals.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Register Globals
3 | isChild: true
4 | anchor: register_globals
5 | ---
6 |
7 | ## Register Globals {#register_globals_title}
8 |
9 | **注意:**
10 | PHP 5.4.0 からは `register_globals`
11 | という設定項目がなくなったので、この設定は使えない。
12 | このページは単に、大昔のアプリケーションをアップグレードしている人たち向けの警告として用意したものでしかない。
13 |
14 | `register_globals`を有効にすると、`$_POST`や`$_GET`そして`$_REQUEST`
15 | などの内容にアプリケーションのグローバルスコープでアクセスできるようになる。
16 | これを使うとセキュリティの問題が発生しやすくなる。
17 | というのも、そのデータがどこからきたものなのかをアプリケーション側で判断できなくなるからだ。
18 |
19 | たとえば `$_GET['foo']` の内容に `$foo` でアクセスできることになるのだが、
20 | これは、宣言済みの変数の中身を自動的に上書きしてしまうことにつながる。
21 | PHP 5.4.0 より前のバージョンを使っている場合は、
22 | __確実に__ `register_globals` を __off__ にしておこう。
23 |
--------------------------------------------------------------------------------
/_posts/10-07-01-Error-Reporting.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: エラーレポート
3 | isChild: true
4 | anchor: error_reporting
5 | ---
6 |
7 | ## エラーレポート {#error_reporting_title}
8 |
9 | エラーを記録しておくと、アプリケーションに何か問題があったときにその原因を見つけやすくなる。
10 | しかしその一方で、アプリケーションの構造に関する情報を外部に公開してしまうことにもなる。
11 | エラーメッセージを出すことで起こる問題からアプリケーションを守るには、
12 | 開発環境と本番環境でサーバーの設定を切り替える必要がある。
13 |
14 | ### 開発環境
15 |
16 | **開発** 環境で、起こりうるエラーをすべて表示するときには、`php.ini`で次のように設定する。
17 |
18 | {% highlight ini %}
19 | display_errors = On
20 | display_startup_errors = On
21 | error_reporting = -1
22 | log_errors = On
23 | {% endhighlight %}
24 |
25 | > 値に`-1`を指定すると、仮に将来のバージョンのPHPで新しいレベルと定数が追加されたとしてもすべてのエラーを表示するようになります。E_ALL 定数も、PHP 5.4以降これと同じ挙動になります。 - [php.net](https://www.php.net/function.error-reporting)
26 |
27 | `E_STRICT`エラーレベル定数は5.3.0で導入されたもので、当時は
28 | `E_ALL`には含まれていなかった。でも5.4.0からは`E_ALL`に含まれるようになった。
29 | だからどうなんだって?
30 | あらゆるエラーを表示させたいときには、5.3の場合は
31 | `-1`あるいは`E_ALL | E_STRICT`を使わないといけないってことだ。
32 |
33 | **PHPのバージョン別の、すべてのエラーを表示させるための設定**
34 |
35 | * < 5.3 `-1` or `E_ALL`
36 | * 5.3 `-1` or `E_ALL | E_STRICT`
37 | * > 5.3 `-1` or `E_ALL`
38 |
39 | ### 本番環境
40 |
41 | **本番** 環境でエラーの情報を見せないようにするには、`php.ini`で次のように設定する。
42 |
43 | {% highlight ini %}
44 | display_errors = Off
45 | display_startup_errors = Off
46 | error_reporting = E_ALL
47 | log_errors = On
48 | {% endhighlight %}
49 |
50 | この本番環境用の設定をしても、ウェブサーバーのエラーログにはエラーの内容がきちんと残る。
51 | しかし、ユーザーにはエラーが見えなくなる。これらの設定項目についてもっと詳しく知りたければ、
52 | PHP のマニュアルを読もう。
53 |
54 | * [error_reporting](https://www.php.net/errorfunc.configuration#ini.error-reporting)
55 | * [display_errors](https://www.php.net/errorfunc.configuration#ini.display-errors)
56 | * [display_startup_errors](https://www.php.net/errorfunc.configuration#ini.display-startup-errors)
57 | * [log_errors](https://www.php.net/errorfunc.configuration#ini.log-errors)
58 |
--------------------------------------------------------------------------------
/_posts/11-01-01-Testing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: テスト
3 | anchor: testing
4 | ---
5 |
6 | # テスト {#testing_title}
7 |
8 | PHPのコードを書くときには、自動化されたテストも書くのがよい習慣だとされている。
9 | そうすれば、頑丈なアプリケーションが作れるようになる。自動テストを活用すれば、
10 | 何かを変更したり機能を追加したりしたときにもアプリケーションがきちんと動くことを確認できる。
11 | 欠かせないツールだ。
12 |
13 | PHP で使えるテスト用ツール (あるいはフレームワーク) にはいくつかのものがあり、
14 | それぞれ異なる手法を使っている。が、目指すところは同じ。
15 | 手作業でのテストをなくす、そして最新の変更で既存の機能を壊していないかどうかを確かめるためだけに
16 | 大規模な品質保証チームを使うなんてことをなくす、というのが目標だ。
17 |
--------------------------------------------------------------------------------
/_posts/11-02-01-Test-Driven-Development.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: テスト駆動開発
3 | isChild: true
4 | anchor: test_driven_development
5 | ---
6 |
7 | ## テスト駆動開発 {#test_driven_development_title}
8 |
9 | [Wikipedia](https://wikipedia.org/wiki/Test-driven_development) によると、
10 |
11 | > Test-driven development (TDD) is a software development process that relies on the repetition of a very short
12 | > development cycle: first the developer writes a failing automated test case that defines a desired improvement or new
13 | > function, then produces code to pass that test and finally refactors the new code to acceptable standards. Kent Beck,
14 | > who is credited with having developed or 'rediscovered' the technique, stated in 2003 that TDD encourages simple
15 | > designs and inspires confidence.
16 |
17 | アプリケーションのテストには、いくつかの種類がある。
18 |
19 | ### ユニットテスト
20 |
21 | ユニットテストとはプログラミングの手法のひとつだ。
22 | 関数やクラスやメソッドが期待通りに動いていることを開発中に常に確かめる。
23 | さまざまな関数やメソッドの入出力の値をチェックすれば、
24 | その内部ロジックが正しく動いていることを確認できる。
25 | 依存性注入の仕組みを利用してクラスのモックやスタブを使えば、
26 | 依存ライブラリが正しく使われていることを確かめられる。
27 |
28 | クラスや関数を作るときに、その振る舞いを確かめるためのユニットテストも同時に作る。
29 | 最も基本的なレベルだと、間違った引数を渡した場合にエラーになることや、
30 | 正しい引数を渡したときに正常に動くことなどを確認しないといけない。
31 | こうしておけば、後にクラスや関数に手を加えたときにも
32 | 今までの機能が期待通りに動くかどうかを確かめられるようになる。
33 | test.php で `var_dump()` とかいうやり方もあるけど、
34 | まともなアプリケーションを作るつもりならそれはあり得ない。
35 |
36 | それ以外にもユニットテストの使い道はある。オープンソースに貢献する手段として使えるのだ。
37 | うまく機能していないことを示すためにテストを書く。そして動くように修正する。
38 | 最後にテストが通ることを確認する。こんなパッチを送れば、きっと受け入れてもらいやすくなるだろう。
39 | もし何かプロジェクトを運営していて pull request を受け付けているのなら、
40 | 「パッチにはテストをつけること」という条件をつけておくといいだろう。
41 |
42 | [PHPUnit](https://phpunit.de)は、PHPアプリケーションでユニットテストを書くための
43 | デファクトスタンダードのフレームワークだ。しかしそれ以外にも選択肢がある。
44 |
45 | * [atoum](https://github.com/atoum/atoum)
46 | * [Kahlan](https://github.com/kahlan/kahlan)
47 | * [Peridot](https://peridot-php.github.io/)
48 | * [Pest](https://pestphp.com/)
49 | * [SimpleTest](https://github.com/simpletest/simpletest)
50 |
51 | ### インテグレーションテスト
52 |
53 | [Wikipedia](https://wikipedia.org/wiki/Integration_testing) によると、
54 |
55 | > Integration testing (sometimes called Integration and Testing, abbreviated "I&T") is the phase in software testing in
56 | > which individual software modules are combined and tested as a group. It occurs after unit testing and before
57 | > validation testing. Integration testing takes as its input modules that have been unit tested, groups them in larger
58 | > aggregates, applies tests defined in an integration test plan to those aggregates, and delivers as its output the
59 | > integrated system ready for system testing.
60 |
61 | ユニットテスト用のツールの多くはインテグレーションテストにも使える。
62 | ほぼ同じような指針で行うものだからである。
63 |
64 | ### 機能テスト
65 |
66 | 受け入れテストと呼ばれることもある。実際にアプリケーションを使う観点での自動テストを作ってその動きを確認する。
67 | 単にコード片が正しく動くとか、個々のパーツがお互いに正しくやりとりできるかとかいうレベルのテストではない。
68 | このレベルのテストでは、実際のデータを使ったりアプリケーションの実際のユーザーをシミュレートしたりすることが一般的だ。
69 |
70 | #### 機能テスト用のツール
71 |
72 | * [Selenium](https://www.selenium.dev/)
73 | * [Mink](https://mink.behat.org/)
74 | * [Codeception](https://codeception.com/) これはフルスタックのテスティングフレームワークで、受け入れテスト用のツール群も含んでいる
75 | * [Storyplayer](https://datasift.github.io/storyplayer/) これはフルスタックのテスティングフレームワークで、テスト環境をオンデマンドで作ったり破棄したりする機能も含んでいる
76 |
--------------------------------------------------------------------------------
/_posts/11-03-01-Behavior-Driven-Development.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 振る舞い駆動開発
3 | isChild: true
4 | anchor: behavior_driven_development
5 | ---
6 |
7 | ## 振る舞い駆動開発 {#behavior_driven_development_title}
8 |
9 | 振る舞い駆動開発 (BDD) には二種類ある。SpecBDD と StoryBDD だ。
10 | SpecBDD はコードの技術的な振る舞いを重視し、StoryBDD は業務的あるいは機能的な振る舞いを重視する。
11 | PHP には、これら二種類の BDD 用のフレームワークが存在する。
12 |
13 | StoryBDD では、人間が読める形式のストーリーを書いてアプリケーションの振る舞いを表す。
14 | そしてそのストーリーを、アプリケーションのテストとして実行する。
15 | PHP アプリケーションで StoryBDD をするために使えるフレームワークが
16 | [Behat] で、これは Ruby の [Cucumber] の影響を受けたフレームワークである。
17 | Gherkin DSL を使ってフィーチャを記述できる。
18 |
19 | SpecBDD では、実際のコードのあるべき振る舞いをスペックとして書く。
20 | 関数やメソッドを単独でテストするのではなく、その関数やメソッドがどのように振る舞うのかを記述するのだ。
21 | PHP で SpecBDD をするときに使えるフレームワークが [PHPSpec] で、これは
22 | Ruby の [RSpec project][Rspec] の影響を受けている。
23 |
24 | ### BDD に関するリンク
25 |
26 | * [Behat] は PHP 用の StoryBDD フレームワークで、Ruby の [Cucumber] プロジェクトの影響を受けている。
27 | * [PHPSpec] は PHP 用の SpecBDD フレームワークで、Ruby の [RSpec] プロジェクトの影響を受けている。
28 | * [Codeception] はフルスタックのテストフレームワークで、BDD の原則に従っている。
29 |
30 |
31 | [Behat]: https://behat.org/
32 | [Cucumber]: https://cucumber.io/
33 | [PHPSpec]: https://www.phpspec.net/
34 | [RSpec]: https://rspec.info/
35 | [Codeception]: https://codeception.com/
36 |
--------------------------------------------------------------------------------
/_posts/11-04-01-Complementary-Testing-Tools.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: その他のテスト用ツール
3 | isChild: true
4 | anchor: complementary_testing_tools
5 | ---
6 |
7 | ## その他のテスト用ツール {#complementary_testing_tools_title}
8 |
9 | これまでに取り上げたテストツールや振る舞い駆動開発フレームワーク以外にも、
10 | いろいろなフレームワークやヘルパーライブラリがある。これらも有用に使える。
11 |
12 | ### ツールへのリンク
13 |
14 | * [Selenium] はブラウザ自動化ツールで、[PHPUnit と組み合わせて使える]。
15 | * [Mockery] はモックオブジェクトフレームワークで、[PHPUnit] や [PHPSpec] と組み合わせて使える。
16 | * [Prophecy] は強力で柔軟な PHP 用モックオブジェクトフレームワークで、[PHPSpec] に組み込まれている。さらに [PHPUnit] と組み合わせても使える。
17 | * [php-mock] はPHPの組み込み関数のモックを作りやすくするためのライブラリだ。
18 | * [Infection] は [ミューテーション解析] をPHPで実装したもので、自分が書いたテストがどれくらい効果的かを測定してくれる。
19 | * [PHPUnit Polyfills] は、様々なPHPUnitのバージョンでテストスイートを実行する必要がある場合に、PHPUnitの複数バージョンで互換性があるテストを生成できるライブラリだ。
20 |
21 |
22 | [Selenium]: https://www.selenium.dev/
23 | [PHPUnit と組み合わせて使える]: https://github.com/giorgiosironi/phpunit-selenium/
24 | [Mockery]: https://github.com/padraic/mockery
25 | [PHPUnit]: https://phpunit.de/
26 | [PHPSpec]: https://www.phpspec.net/
27 | [Prophecy]: https://github.com/phpspec/prophecy
28 | [php-mock]: https://github.com/php-mock/php-mock
29 | [Infection]: https://github.com/infection/infection
30 | [ミューテーション解析]: https://ja.wikipedia.org/wiki/%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E8%A7%A3%E6%9E%90
31 | [PHPUnit Polyfills]: https://github.com/Yoast/PHPUnit-Polyfills
32 |
--------------------------------------------------------------------------------
/_posts/12-01-01-Servers-and-Deployment.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 各種サーバーへのデプロイ
3 | anchor: servers_and_deployment
4 | ---
5 |
6 | # 各種サーバーへのデプロイ {#servers_and_deployment_title}
7 |
8 | PHP のアプリケーションをデプロイして本番サーバーで運用するための方法を紹介する。
9 |
--------------------------------------------------------------------------------
/_posts/12-02-01-Platform-as-a-Service.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Platform as a Service (PaaS)
3 | isChild: true
4 | anchor: platform_as_a_service
5 | ---
6 |
7 | ## Platform as a Service (PaaS) {#platform_as_a_service_title}
8 |
9 | PaaS を使えば、PHP アプリケーションをウェブ上で動かすために必要なシステムやネットワーク環境を用意してくれる。
10 | ほとんど何も設定せずに、PHP のアプリケーションやフレームワークを実行できるということだ。
11 |
12 | 最近は、PHP アプリケーションのデプロイ先として PaaS を使うことが多くなった。あらゆる規模のアプリケーションを扱える。
13 | [PHP 用の PaaS "Platform as a Service" プロバイダ](#php_paas_providers) を、[情報源](#resources)
14 | にまとめた。
15 |
--------------------------------------------------------------------------------
/_posts/12-03-01-Virtual-or-Dedicated-Servers.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 仮想サーバー(専用サーバー)
3 | isChild: true
4 | anchor: virtual_or_dedicated_servers
5 | ---
6 |
7 | ## 仮想サーバーあるいは専用サーバー {#virtual_or_dedicated_servers_title}
8 |
9 | サーバー管理が苦にならない人、あるいはサーバー管理を勉強してみたい人は、仮想サーバーあるいは専用サーバーを選ぶといい。
10 | そうすれば、アプリケーションの運用環境を完全に制御できる。
11 |
12 | ### nginx と PHP-FPM
13 |
14 | PHP に組み込まれた FastCGI Process Manager (FPM) は [nginx]
15 | と組み合わせるのに最適だ。nginx は、軽量でパフォーマンスに優れたウェブサーバーである。
16 | Apache よりも少ないメモリで動き、同時にさばけるリクエストの数も多い。
17 | これは特に、共有メモリの少ない仮想サーバーでは重要だ。
18 |
19 | * [nginx][nginx]
20 | * [PHP-FPM][phpfpm]
21 | * [nginx と PHP-FPM で安全な環境を作る][secure-nginx-phpfpm]
22 |
23 | ### Apache と PHP
24 |
25 | PHP と Apache は長い付き合いだ。
26 | Apache はいろんな設定が可能で、さまざまな [モジュール][apache-modules]
27 | で機能を拡張できる。共有サーバーで PHP のフレームワークを動かしたり、WordPress
28 | みたいなアプリケーションを動かしたりするときにはよく使われる選択肢だ。
29 | 残念ながら Apache は、デフォルトでは nginx よりもメモリを食うし、
30 | 同時にさばけるユーザー数も nginx より少ない。
31 |
32 | Apache で PHP を動かすにはいくつかの選択肢がある。
33 | 一番よく使われていて簡単に設定できるのが、[prefork MPM] と `mod_php` の組み合わせだ。
34 | メモリの使用効率はそれほどよくないが、とりあえず動かして使うには一番シンプルだ。
35 | サーバー管理方面にあまり足を突っ込みたくない場合は、この方法がいいだろう。
36 | 注意すべき点は、`mod_php` を使う場合は必ず prefork MPM を使わないといけないということだ。
37 |
38 | Apache 本来のパフォーマンスや安定性をもっと絞り出したいという場合は、nginx と同じように FPM を使うこともできる。
39 | この場合は、[worker MPM] あるいは
40 | [event MPM] に mod_fastcgi あるいは mod_fcgid
41 | を組み合わせる。この設定はメモリの利用効率がよくて高速に動作するが、設定に手間がかかる。
42 |
43 | Apache 2.4 以降なら、[mod_proxy_fcgi]が使える。簡単にセットアップできるし高性能だ。
44 |
45 | * [Apache][apache]
46 | * [Multi-Processing Modules][apache-MPM]
47 | * [mod_fastcgi][mod_fastcgi]
48 | * [mod_fcgid][mod_fcgid]
49 | * [mod_proxy_fcgi][mod_proxy_fcgi]
50 | * [Apache、PHP-FPM、mod_proxy_fcgiの環境のセットアップ][tutorial-mod_proxy_fcgi]
51 |
52 |
53 | [nginx]: https://nginx.org/
54 | [phpfpm]: https://www.php.net/install.fpm
55 | [secure-nginx-phpfpm]: https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/
56 | [apache-modules]: https://httpd.apache.org/docs/2.4/mod/
57 | [prefork MPM]: https://httpd.apache.org/docs/2.4/mod/prefork.html
58 | [worker MPM]: https://httpd.apache.org/docs/2.4/mod/worker.html
59 | [event MPM]: https://httpd.apache.org/docs/2.4/mod/event.html
60 | [apache]: https://httpd.apache.org/
61 | [apache-MPM]: https://httpd.apache.org/docs/2.4/mod/mpm_common.html
62 | [mod_fastcgi]: https://blogs.oracle.com/opal/post/php-fpm-fastcgi-process-manager-with-apache-2
63 | [mod_fcgid]: https://httpd.apache.org/mod_fcgid/
64 | [mod_proxy_fcgi]: https://httpd.apache.org/docs/current/mod/mod_proxy_fcgi.html
65 | [tutorial-mod_proxy_fcgi]: https://serversforhackers.com/video/apache-and-php-fpm
66 |
--------------------------------------------------------------------------------
/_posts/12-04-01-Shared-Servers.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 共有サーバー
3 | isChild: true
4 | anchor: shared_servers
5 | ---
6 |
7 | ## 共有サーバー {#shared_servers_title}
8 |
9 | PHP の人気のおかげで、いろんな共有サーバーで PHP が使える。
10 | むしろ PHP が使えない共有サーバーを見つけるほうが難しいだろう。
11 | ただし、最新バージョンが使えるかどうかは要注意だ。
12 | 共有サーバーでは、あなただけでなく他の開発者も同じマシンにウェブサイトをデプロイする。
13 | その利点は、安上がりに使えるということだ。
14 | ただ欠点もあって、同じサーバーに同居しているお隣さんが何をしでかすかがわからない。
15 | めちゃめちゃ負荷のかかることをしてしまったり、セキュリティホールを作り込んでしまったりといった恐れがある。
16 | もし充分な予算があるのなら、できるだけ共有サーバーは避けよう。
17 |
18 | 共有サーバーでは、最新バージョンのPHPが使えることを必ず確認すること。
19 |
--------------------------------------------------------------------------------
/_posts/12-05-01-Building-your-Application.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: アプリケーションのビルドとデプロイ
3 | isChild: true
4 | anchor: building_and_deploying_your_application
5 | ---
6 |
7 | ## アプリケーションのビルドとデプロイ {#building_and_deploying_your_application_title}
8 |
9 | まさか、データベースのスキーマを変更したりテストを実行したりとかいったことを手作業でやってるなんてことはないよね?
10 | ちょっと待った!新しいバージョンのアプリケーションをデプロイするときに手作業がひとつでも増えると、
11 | 致命的な間違いを犯してしまう可能性もそのぶん増えてしまうんだ。たとえ単純な更新作業だとしても、
12 | きちんとしたビルド手順にしたがうこと。継続的インテグレーションの戦略にしたがって、
13 | [ビルドの自動化][buildautomation] をしておくといい。
14 |
15 | 自動化できるタスクには、こんなものがある。
16 |
17 | * 依存関係の管理
18 | * 必要な資産のコンパイル
19 | * テストの実行
20 | * ドキュメントの生成
21 | * パッケージング
22 | * デプロイ
23 |
24 |
25 | ### デプロイツール
26 |
27 | デプロイツールとは、ソフトウェアのデプロイにからむありがちな作業を処理するスクリプトをまとめたものだと言える。
28 | デプロイツール自体は君が作るソフトウェアの一部ではない。ソフトウェアを「外部から」支援するものだ。
29 |
30 | ビルド自動化やデプロイの助けとなるオープンソースのツールがたくさん公開されている。PHPで書かれているものもあれば、
31 | そうじゃないものもある。PHP製じゃないからといって、それを使わない理由はない。
32 | もし自分のやりたいことに適したツールがあるのなら、使うべきだ。いくつか例をあげよう。
33 |
34 | [Phing] を使えば、パッケージングやデプロイそしてテストといった処理をシンプルなXMLビルドファイルで設定できる。
35 | Phingは[Apache Ant] をベースに作られたもので、
36 | Webアプリのインストールやアップデートに必要となるタスク群を提供する。
37 | カスタムタスクで機能を追加することもでき、カスタムタスクはPHPで書ける。
38 | 古くからあるツールだけあって堅実で安定してるけど、ちょっと古臭さも感じる
39 | (設定をXMLファイルで管理するところとかね)。
40 |
41 | [Capistrano] は
42 | *中級から上級のプログラマー* 向けのシステムだ。構造化された、繰り返し可能な形式で、
43 | 複数のリモートマシン上でコマンドを実行できる。
44 | Ruby on Railsのアプリをデプロイするように設定されているが、
45 | PHP のアプリもデプロイできる。
46 | Capistranoを使いこなすには、RubyとRakeに関するそれなりの知識が必要だ。
47 | Dave Gardnerのblog記事[PHP Deployment with Capistrano][phpdeploy_capistrano]
48 | は、Capistranoに興味のあるPHP開発者への入門記事としておすすめだ。
49 |
50 | [Ansistrano] はデプロイプロセス(デプロイやロールバック) を簡単に管理するための Ansible のロールだ。PHP や Python や Ruby のようなスクリプト言語で書かれたアプリケーションで使える。これは、[Capistrano] を Ansible に移植したもので、既にたくさんの PHP を利用している会社で使われている。
51 |
52 | [Deployer] はPHPで書かれたデプロイツールで、シンプルかつ機能的だ。
53 | タスクを並列に実行し、アトミックなデプロイを行い、サーバー間の整合性を維持する。
54 | SymfonyやLaravel、Zend Framework、そしてYiiなどで使える、一般的なタスクのレシピが用意されている。
55 | Younes Rafieの記事[Easy Deployment of PHP Applications with Deployer][phpdeploy_deployer]
56 | は、Deployerを使ってアプリケーションをデプロイするためのよいチュートリアルになっている。
57 |
58 | [Magallanes] もPHPで書かれたツールで、YAMLでのシンプルな設定ができる。
59 | 複数サーバーや複数環境、アトミックなデプロイに対応していて、
60 | 一般的なツールやフレームワークで使える組み込みのタスクが用意されている。
61 |
62 | #### あわせて読みたい:
63 |
64 | * [Apache Antによるプロジェクトの自動化][apache_ant_tutorial]
65 | * [Expert PHP Deployments][expert_php_deployments] - CapistranoやPhing、Vagrantによるデプロイを扱ったフリーの書籍
66 | * [Deploying PHP Applications][deploying_php_applications] - PHPのデプロイに関するベストプラクティスやツールを扱った書籍
67 |
68 | ### サーバーの構成管理
69 |
70 | サーバーの構成管理は、大量のサーバーを扱うようになると特に大変なタスクだ。
71 | いろんなツールが用意されているので、こういったインフラの構築を自動化できる。
72 | ツールを使えば、適切なサーバーが適切な構成になっていることを確実にできる。
73 | これらのツールは大規模なクラウドホスティングプロバイダー
74 | (Amazon Web Services, Heroku, DigitalOceanなど)
75 | にもインスタンスの管理用に統合されていることが多くて、
76 | より簡単にアプリケーションをスケールできるようになっている。
77 |
78 | [Ansible] は、YAMLファイルでインフラを管理するツールだ。
79 | 気軽に使い始められるし、複雑で大規模なアプリケーションにも使える。
80 | クラウドのインスタンスを管理するためのAPIも用意されていて、
81 | 対応したツールを使えば動的インベントリを通じてインスタンスを管理できる。
82 |
83 | [Puppet] は、独自の言語やファイルタイプを使ってサーバーや構成を管理する。
84 | マスター/クライアント形式で使うこともできるし、「マスターレス」モードで使うこともできる。
85 | マスター/クライアントモードの場合は、所定のインターバルでクライアントが中央サーバーをポーリングして、
86 | 新しい構成が見つかったら自分自身を更新する。
87 | マスターレスモードでは、変更内容を各ノードにプッシュする。
88 |
89 | [Chef] はRubyで作られた強力なシステムインテグレーションフレームワークで、
90 | サーバー環境や仮想マシンをまるごと構築できる。
91 | Amazon Web Servicesとも統合されていて、OpsWorksというサービスを通じて利用する。
92 |
93 | #### あわせて読みたい:
94 |
95 | * [Ansibleチュートリアル][an_ansible_tutorial]
96 | * [Ansible for DevOps][ansible_for_devops] - Ansibleのすべてを扱った書籍
97 | * [Ansible for AWS][ansible_for_aws] - AnsibleとAmazon Web Servicesとの統合について扱った書籍
98 | * [LAMPアプリケーションのデプロイにChefやVagrantそしてEC2を使うというお題で書かれた全3回のシリーズ][chef_vagrant_and_ec2]
99 | * [Chefのクックブック。PHPのインストールと設定やPEARについて扱っている][Chef_cookbook]
100 | * [Chefのビデオチュートリアルシリーズ][Chef_tutorial]
101 |
102 | ### 継続的インテグレーション
103 |
104 | > 継続的インテグレーションはソフトウェア開発のプラクティスのひとつで、
105 | > チームのメンバーが自分たちの作業を頻繁に統合するというものだ。
106 | > 通常は、各自が少なくとも一日に一度は統合する。一日に何度も統合することもある。
107 | > 多くのチームが、この方針のおかげで統合時の問題が少なくなるし、
108 | > きちんとしたソフトウェアをより素早く開発できるようになると実感している。
109 |
110 | *-- マーティン・ファウラー*
111 |
112 | PHPで継続的インテグレーションを実践する方法はいろいろある。
113 | [Travis CI] のおかげで、
114 | ちょっとしたプロジェクトにも簡単に継続的インテグレーションを組み込めるようになった。
115 | Travis CIは継続的インテグレーションのホスティング環境で、オープンソースコミュニティに開放されている。
116 | GitHubと統合されており、PHPを含むさまざまな言語に対応している。
117 | GitHub は、継続的インテグレーションのワークフローとして [GitHub Actions][github_actions] を提供している。
118 |
119 | #### あわせて読みたい
120 |
121 | * [Jenkinsによる継続的インテグレーション][Jenkins]
122 | * [PHPCIによる継続的インテグレーション][PHPCI]
123 | * [PHP Censorによる継続的インテグレーション][PHP Censor]
124 | * [Teamcityによる継続的インテグレーション][Teamcity]
125 |
126 | [buildautomation]: https://wikipedia.org/wiki/Build_automation
127 | [Phing]: https://www.phing.info/
128 | [Apache Ant]: https://ant.apache.org/
129 | [Capistrano]: https://capistranorb.com/
130 | [Ansistrano]: https://ansistrano.com
131 | [phpdeploy_deployer]: https://www.sitepoint.com/deploying-php-applications-with-deployer/
132 | [Chef]: https://www.chef.io/
133 | [chef_vagrant_and_ec2]: https://web.archive.org/web/20190307220000/http://www.jasongrimes.org/2012/06/managing-lamp-environments-with-chef-vagrant-and-ec2-1-of-3/
134 | [Chef_cookbook]: https://github.com/sous-chefs/php
135 | [Chef_tutorial]: https://www.youtube.com/playlist?list=PL11cZfNdwNyNYcpntVe6js-prb80LBZuc
136 | [apache_ant_tutorial]: https://code.tutsplus.com/tutorials/automate-your-projects-with-apache-ant--net-18595
137 | [Travis CI]: https://www.travis-ci.com/
138 | [Jenkins]: https://jenkins.io/
139 | [PHPCI]: https://github.com/dancryer/phpci
140 | [PHP Censor]: https://github.com/php-censor/php-censor
141 | [Teamcity]: https://www.jetbrains.com/teamcity/
142 | [Deployer]: https://deployer.org/
143 | [Magallanes]: https://www.magephp.com/
144 | [deploying_php_applications]: https://deployingphpapplications.com/
145 | [Ansible]: https://www.ansible.com/
146 | [Puppet]: https://puppet.com/
147 | [ansible_for_devops]: https://leanpub.com/ansible-for-devops
148 | [ansible_for_aws]: https://leanpub.com/ansible-for-aws
149 | [an_ansible_tutorial]: https://serversforhackers.com/an-ansible-tutorial
150 | [github_actions]: https://docs.github.com/en/actions
151 |
--------------------------------------------------------------------------------
/_posts/13-01-01-Virtualization.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 仮想化
3 | anchor: virtualization
4 | ---
5 |
6 | # 仮想化 {#virtualization_title}
7 |
8 | 開発環境だの本番環境だの、いろいろ違う環境でアプリケーションを動かしていると、おかしなバグに出くわしてしまいかねない。
9 | 「開発環境だと問題ないのに、本番環境だと動かない」みたいなやつだ。
10 | また、チームで開発しているときに、開発環境のいろんなライブラリのバージョンをきちんと統一しておくのも、面倒だ。
11 |
12 | Windowsで開発してLinux(などWindows以外の環境)にデプロイしていたり、チームで開発していたりする場合は、
13 | 仮想マシンを使うことを考えるべきだ。
14 | 何も難しいことはない。VMwareやVirtualBoxみたいな有名どころだけでなく、
15 | 仮想環境を簡単に準備するためのツールも用意されている。
16 |
--------------------------------------------------------------------------------
/_posts/13-02-01-Vagrant.md:
--------------------------------------------------------------------------------
1 | ---
2 | isChild: true
3 | anchor: vagrant
4 | ---
5 |
6 | ## Vagrant {#vagrant_title}
7 |
8 | [Vagrant]を使えば、既知の仮想環境を使って自分用のボックスを作れて、その環境の構成も、たったひとつの設定ファイルだけでできる。
9 | このボックスを手動で設定することもできるし、
10 | [Puppet]とか[Chef]みたいな「プロビジョニング」ソフトにおまかせすることだってできる。
11 | ベースとなる環境を配布できるようにしておけば、複数の開発環境をまったく同じ状態に構築できる。
12 | 複雑怪奇なコマンドを羅列した「環境構築手順書」だとかいうのもいらなくなるってこと。
13 | ベース環境を「破棄」したり作りなおしたりするのもそんなに手間がかからないので、
14 | まっさらな環境を用意するのもお手軽にできる。
15 |
16 | Vagrantは、フォルダを作って、ホストと仮想マシンの間でコードを共有する。
17 | つまり、ホストマシンで作ったり編集したりしたコードを、そのまま仮想マシンの中で実行できるっていうことだ。
18 |
19 | [Vagrant]: https://www.vagrantup.com/
20 | [Puppet]: https://puppet.com/
21 | [Chef]: https://www.chef.io/
22 |
--------------------------------------------------------------------------------
/_posts/13-03-01-Docker.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Docker
3 | isChild: true
4 | anchor: docker
5 | ---
6 |
7 | ## Docker {#docker_title}
8 |
9 | [Docker] - 完全仮想化されたマシンの軽量な代替 - は、すべてが "コンテナ" であるため、そのように呼ばれている。コンテナは、もっとも簡単なケースでは、特定のタスクをひとつだけ(たとえば Webサーバー)を実行するビルディングブロックだ。"イメージ" は、コンテナをビルドするために使うパッケージだ。Docker は、イメージを配布するためのリポジトリを持っている。
10 |
11 | 典型的な LAMP アプリケーションの場合、コンテナは3つ必要になる。Webサーバー、PHP-FPM プロセス、そして MySQL のコンテナだ。共有フォルダを Vagrant 内で使うのと同じように、アプリケーションのファイルはそのままで、Docker にどこを探せばよいのかを指示することができる。
12 |
13 | コンテナはコマンドライン (以下の例を見よう) から作成することもできるし、メンテナンスを簡単にするために `docker-compose.yml` ファイルをあなたのプロジェクト向けにビルドすることもできる。このファイルでは、何のコンテナを作るかを指定し、それらが互いにどう通信するのかを指示する。
14 |
15 | Docker は、複数の Web サイト を開発し、仮想マシン上でそれぞれの Webサイト のインストール環境を分離したい場合に役立つ。だが、すべてを最新の環境にしておくために、追加のディスクスペースや時間は必要ない。Docker は効率的だ: イメージのダウンロードやインストールは高速だし、何度イメージが必要になっても、保存しておく必要があるイメージのコピーはひとつだけだ。また、同じ OS のカーネルを共有するので、必要なメモリは少なくて済む。よって、多くのサーバを同時に実行できるし、停止や起動に掛かる時間も秒単位で済む。サーバマシンが完全に起動するのを待つ必要がないのだ。
16 |
17 | ### 例:PHPアプリケーションをDockerで実行する
18 |
19 | マシンに[Dockerをインストール][docker-install]したら、あと一手間だけで、PHP が使える Apache 環境を用意できる。
20 | 次のコマンドは、最新版のPHP入りのApache環境をダウンロードして、
21 | ディレクトリ `/path/to/your/php/files` を `http://localhost:8080` で見られるようにするものだ。
22 |
23 | {% highlight console %}
24 | docker run -d --name my-php-webserver -p 8080:80 -v /path/to/your/php/files:/var/www/html/ php:apache
25 | {% endhighlight %}
26 |
27 | これで、コンテナを初期化して実行できる。`-d` は、バックグラウンドで実行するオプションだ。
28 | コンテナを停止したり、再開したりしたくなったりした場合は、nameに指定した名前を使って
29 | `docker stop my-php-webserver` や `docker start my-php-webserver` などとするだけだ。
30 | さっきのパラメータを何度も指定する必要はない。
31 |
32 | ### Dockerについてもっと知りたい
33 |
34 | ここで紹介したのは、基本的なサーバーをお手軽に実行するためのコマンドだ。
35 | だけど、Docker にできることはまだまだたくさんある(そして、たくさんのビルド済みのイメージが [Docker Hub][docker-hub] にある)。Docker を最大限活用するために、[Docker ユーザーガイド][docker-doc] を読み、時間を掛けてそれらに関する用語を学んでいこう。そして、ダウンロードしたコードを安全かどうか確認せずに実行しないように注意しよう。非公式なイメージには最新のセキュリティパッチが当たっていない場合があるからだ。よくわからなければ、[オフィシャルのリポジトリ][docker-hub-official] を常に使うようにしよう。
36 |
37 | [PHPDocker.io] サイトは、あなたが選んだ PHP のバージョンや拡張機能を含めた形で、全ての機能を備えた LAMP/LEMP スタックに必要なファイルを全て自動生成してくれる。
38 |
39 | * [Docker 公式サイト][docker]
40 | * [Docker のインストール][docker-install]
41 | * [Docker ユーザーガイド][docker-doc]
42 | * [Docker Hub][docker-hub]
43 | * [Docker Hub - official images][docker-hub-official]
44 |
45 | [Docker]: https://www.docker.com/
46 | [docker-hub]: https://hub.docker.com/
47 | [docker-hub-official]: https://hub.docker.com/explore/
48 | [docker-install]: https://docs.docker.com/get-docker/
49 | [docker-doc]: https://docs.docker.com/
50 | [PHPDocker.io]: https://phpdocker.io/
51 |
--------------------------------------------------------------------------------
/_posts/14-01-01-Caching.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: キャッシュ
3 | anchor: caching
4 | ---
5 |
6 | # キャッシュ {#caching_title}
7 |
8 | PHP 自体は極めて高速だけど、リモート接続やファイルの読み込みなどが絡むとボトルネックになるかもしれない。
9 | ありがたいことに、いろんなツールを活用すればアプリケーションを高速化できるし、
10 | 時間のかかる処理の実行回数を減らすこともできる。
11 |
--------------------------------------------------------------------------------
/_posts/14-02-01-Opcode-Cache.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: オペコードキャッシュ
3 | isChild: true
4 | anchor: opcode_cache
5 | ---
6 |
7 | ## オペコードキャッシュ {#opcode_cache_title}
8 |
9 | PHPファイルを実行するときには、まずそれを[オペコード](https://php-legacy-docs.zend.com/manual/php4/en/internals2.opcodes) (CPU用の機械語の指示)
10 | にコンパイルしなければいけない。
11 | ソースコードに変更がなければ、オペコードも同じものになる。
12 | ということは、PHP ファイルに変更がなければコンパイル処理は CPU リソースの無駄遣いになるということだ。
13 |
14 | オペコードキャッシュは、コンパイル済みのオペコードをメモリに格納し、それ以降の呼び出しで再利用することで、
15 | 冗長なコンパイルを回避している。よくあるのは、ファイルのシグネチャや更新時刻をチェックして変更の有無を判断する方式だ。
16 |
17 | オペコードキャッシュを使えば、アプリケーションの実行速度が相当向上する可能性がある。
18 | PHP 5.5 からは、 [Zend OPcache][opcache-book] というオペコードキャッシュが標準で組み込まれるようになった。
19 | 使っている PHP パッケージやディストリビューションにもよるけど、普通はデフォルトで有効になっていることが多い。
20 | [opcache.enable](https://www.php.net/manual/opcache.configuration.php#ini.opcache.enable)
21 | や、 `phpinfo()` の出力で確認しよう。
22 | 古いバージョンのPHPなら、PECL の拡張モジュールが使える。
23 |
24 | オペコードキャッシュについて詳しく知りたければ、以下を参照すること。
25 |
26 | * [Zend OPcache][opcache-book] (PHP 5.5 以降に組み込まれている)
27 | * Zend OPcache (元 Zend Optimizer+) は [オープンソースになった][Zend Optimizer+]
28 | * [WinCache] (Microsoft Windows Server 用の拡張)
29 | * [Wikipediaにおける、PHPアクセラレータの一覧][PHP_accelerators]
30 | * [コードの事前ロード] - PHP >= 7.4
31 |
32 |
33 | [opcache-book]: https://www.php.net/book.opcache
34 | [Zend Optimizer+]: https://github.com/zendtech/ZendOptimizerPlus
35 | [WinCache]: https://www.iis.net/downloads/microsoft/wincache-extension
36 | [PHP_accelerators]: https://wikipedia.org/wiki/List_of_PHP_accelerators
37 | [コードの事前ロード]: https://www.php.net/opcache.preloading
38 |
--------------------------------------------------------------------------------
/_posts/14-03-01-Object-Caching.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: オブジェクトキャッシュ
3 | isChild: true
4 | anchor: object_caching
5 | ---
6 |
7 | ## オブジェクトキャッシュ {#object_caching_title}
8 |
9 | コード内の個々のオブジェクトをキャッシュできれば便利なこともある。
10 | 持ってくるのにコストがかかるデータや、結果がほとんど変わらないデータベースへの問い合わせなどだ。
11 | オブジェクトキャッシュ用のソフトウェアを使えば、こういったデータをメモリ上に保持でき、
12 | その後のアクセスの高速化につながる。
13 | 一度取得したデータをどこかに格納しておいて、それ以降のリクエストではそこから直接取り出す。
14 | そうすればパフォーマンスは劇的に向上するし、データベースサーバーにかかる負荷も減らせる。
15 |
16 | バイトコードキャッシュ用ソリューションの多くはカスタムデータも同様にキャッシュできるので、さらに活用できる。
17 | APCu や WinCache は API を提供しており、PHP コードのデータをメモリキャッシュに格納できる。
18 |
19 | オブジェクトキャッシュシステムとして最もよく使われているのは APCu と memcached だ。
20 | APCu はオブジェクトキャッシュの選択肢として最適で、
21 | シンプルな API を使ってデータをメモリキャッシュに格納できる。
22 | セットアップも簡単で、すぐに使えるようになる。
23 | APCu の制約のひとつは、インストールしたサーバーと密接に結合してしまうことだ。
24 | 一方、Memcached は個別のサービスとしてインストールするものであり、
25 | ネットワーク越しにアクセスできる。つまり、
26 | 中央で管理している超高速なストレージにオブジェクトを格納して、
27 | いろんなシステムからそれを取り出せるということだ。
28 |
29 | PHPを(Fast-)CGIアプリケーションとしてウェブサーバーで実行するときには、
30 | すべてのPHPプロセスが自身のキャッシュを持つことになる。つまり
31 | APCuのデータもワーカープロセス間では共有されないということだ。
32 | こんなときには、かわりにmemcachedを検討すればいい。
33 | こっちはPHPのプロセスには結びついていない。
34 |
35 | ネットワーク環境において、アクセス速度の面では APCu のほうが memcached より優れている。
36 | しかし、memcached のほうが、より手軽にスケールアップできる。
37 | 複数のサーバーを使う予定がないとか memcached の追加機能が不要だという場合は、
38 | オブジェクトキャッシュに APCu を選ぶのが最適だろう。
39 |
40 | APCu を使うロジックの例を示す。
41 |
42 | {% highlight php %}
43 |
19 | * @link https://www.phpdoc.org/docs/latest/index.html
20 | */
21 | class DateTimeHelper
22 | {
23 | /**
24 | * @param mixed $anything Anything that we can convert to a \DateTime object
25 | *
26 | * @throws \InvalidArgumentException
27 | *
28 | * @return \DateTime
29 | */
30 | public function dateTimeFromAnything($anything)
31 | {
32 | $type = gettype($anything);
33 |
34 | switch ($type) {
35 | // Some code that tries to return a \DateTime object
36 | }
37 |
38 | throw new \InvalidArgumentException(
39 | "Failed Converting param of type '{$type}' to DateTime object"
40 | );
41 | }
42 |
43 | /**
44 | * @param mixed $date Anything that we can convert to a \DateTime object
45 | *
46 | * @return void
47 | */
48 | public function printISO8601Date($date)
49 | {
50 | echo $this->dateTimeFromAnything($date)->format('c');
51 | }
52 |
53 | /**
54 | * @param mixed $date Anything that we can convert to a \DateTime object
55 | */
56 | public function printRFC2822Date($date)
57 | {
58 | echo $this->dateTimeFromAnything($date)->format('r');
59 | }
60 | }
61 | {% endhighlight %}
62 |
63 | クラス全体のドキュメントの最初にあるのが [@author] タグと [@link] タグだ。
64 | [@author] タグは、コードの作者を表す。作者が複数いる場合は、何度も繰り返し使ってもかまわない。
65 | [@link] タグは、そのコードに関連するウェブサイトへのリンクを示すために使う。
66 |
67 | クラスの中に目を移すと、最初のメソッドには [@param] タグが記されている。
68 | これは、このメソッドに渡すパラメータの型と名前そして説明を記述するものだ。
69 | さらに、[@return] タグと
70 | [@throws] タグも書かれている。
71 | これらはそれぞれ、戻り値の型と、発生する可能性のある例外を記述するものだ。
72 |
73 | 二番目と三番目のメソッドはほぼ同じだ。まず、最初のメソッドと同様に [@param] タグが書かれている。
74 | 二番目と三番目のメソッドの大きな違いは、 [@return] タグの有無だ。
75 | `@return void` は、このメソッドが何も戻さないことを明示している。
76 | 一方、歴史的に、 `@return void` を省略した場合も同じ意味(何も値を戻さない)になる。
77 |
78 |
79 | [tags]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/index.html
80 | [PHPDoc のマニュアル]: https://docs.phpdoc.org/latest/index.html
81 | [@author]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/author.html
82 | [@link]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/link.html
83 | [@param]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/param.html
84 | [@return]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/return.html
85 | [@throws]: https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/throws.html
86 |
--------------------------------------------------------------------------------
/_posts/16-01-01-Resources.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 情報源
3 | anchor: resources
4 | ---
5 |
6 | # 情報源 {#resources_title}
7 |
--------------------------------------------------------------------------------
/_posts/16-02-01-From-the-Source.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ソースから
3 | isChild: true
4 | anchor: from_the_source
5 | ---
6 |
7 | ## ソースから {#from_the_source_title}
8 |
9 | * [PHP のサイト](https://www.php.net/)
10 | * [PHP のドキュメント](https://www.php.net/docs.php)
11 |
--------------------------------------------------------------------------------
/_posts/16-03-01-People-to-Follow.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: フォローすべき人たち
3 | isChild: true
4 | anchor: people_to_follow
5 | ---
6 |
7 | ## フォローすべき人たち {#people_to_follow_title}
8 |
9 | PHPコミュニティの中で注目すべき人を見つけるのは、コミュニティに加わったばかりの人たちにとっては難しいものだ。
10 | PHPコミュニティの主要メンバーの一覧が、ここにまとまっている。
11 |
12 | *
13 | *
14 |
--------------------------------------------------------------------------------
/_posts/16-04-01-Mentoring.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: メンタリング
3 | isChild: true
4 | anchor: mentoring
5 | ---
6 |
7 | ## メンタリング {#mentoring_title}
8 |
9 | * [php-mentoring.org](https://php-mentoring.org/) - Formal, peer to peer mentoring in the PHP community.
10 |
--------------------------------------------------------------------------------
/_posts/16-05-01-PHP-PaaS-Providers.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: PHP PaaS プロバイダ
3 | isChild: true
4 | anchor: php_paas_providers
5 | ---
6 |
7 | ## PHP PaaS プロバイダ {#php_paas_providers_title}
8 |
9 | * [AppFog](https://www.ctl.io/appfog/)
10 | * [Amezmo](https://www.amezmo.com)
11 | * [AWS Elastic Beanstalk](https://aws.amazon.com/elasticbeanstalk/)
12 | * [Cloudways](https://www.cloudways.com/)
13 | * [Divio](https://www.divio.com/php/)
14 | * [Engine Yard Cloud](https://www.engineyard.com/)
15 | * [fortrabbit](https://www.fortrabbit.com/)
16 | * [Google App Engine](https://cloud.google.com/appengine/docs/php/)
17 | * [Heroku](https://devcenter.heroku.com/categories/php-support)
18 | * [IBM Cloud](https://console.bluemix.net/docs/runtimes/php/getting-started.html#getting_started)
19 | * [Jelastic](https://jelastic.com/)
20 | * [Microsoft Azure](https://azure.microsoft.com/)
21 | * [Nanobox](https://nanobox.io/)
22 | * [Pivotal Web Services](https://run.pivotal.io/)
23 | * [Platform.sh](https://platform.sh/)
24 | * [Red Hat OpenShift](https://www.openshift.com/)
25 |
--------------------------------------------------------------------------------
/_posts/16-06-01-Frameworks.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: フレームワーク
3 | isChild: true
4 | anchor: frameworks
5 | ---
6 |
7 | # フレームワーク {#frameworks_title}
8 |
9 | PHP 開発者の多くは、ウェブアプリケーションを作るときに車輪の再発明を避けてフレームワークを使っている。
10 | フレームワークを使えば低レベルの作業の多くを抽象化でき、
11 | 便利で使いやすいインターフェイスでよくある作業をこなせる。
12 |
13 | 別に、フレームワークを使わないといけないというわけじゃない。
14 | ふつうに PHP で書くべき場面だってある。
15 | もしフレームワークを使うのなら、フレームワークはこんな感じに分類できることを知っておこう。
16 |
17 | * マイクロフレームワーク
18 | * フルスタックフレームワーク
19 | * コンポーネントフレームワーク
20 |
21 | マイクロフレームワークというのは本質的にはラッパーで、
22 | HTTP リクエストを手っ取り早くコールバックに振り分けるだけのものだ。
23 | 場合によっては、開発を支援するためのちょっとした追加ライブラリが付属することもある。
24 | データベースの基本的なラッパーなどだ。マイクロフレームワークの主な使い道は、
25 | リモート HTTP サービスの構築である。
26 |
27 | 多くのフレームワークは、マイクロフレームワークが持つ機能に加えて大量の機能を用意している。
28 | この種のフレームワークのことを、フルスタックフレームワークと呼ぶ。
29 | ORM や認証パッケージなどが含まれることが多い。
30 |
31 | コンポーネントフレームワークとは、特定の目的のための専用ライブラリをとりまとめたフレームワークである。
32 | この種のフレームワークのコンポーネントを各種組み合わせて、
33 | マイクロフレームワークやフルスタックフレームワークを作ることもできる。
34 |
--------------------------------------------------------------------------------
/_posts/16-07-01-Components.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: コンポーネント
3 | isChild: true
4 | anchor: components
5 | ---
6 |
7 | ## コンポーネント {#components_title}
8 |
9 | 先ほど説明したように、「コンポーネント」っていうのは
10 | 共有するコードを作ったりそれを配布したりするための手段のひとつだ。
11 | コンポーネントを登録するリポジトリにもいろいろあるけど、中でも有名なのがこのふたつだ。
12 |
13 | * [Packagist]
14 | * [PEAR]
15 |
16 | どちらのリポジトリについてもコマンドラインのツールが存在し、
17 | コンポーネントのインストールやアップグレードを簡単にできる。
18 | 詳細は[依存関係の管理][dm]を参照すること。
19 |
20 | コンポーネントベースのフレームワークもあれば、フレームワークを持たずにコンポーネントだけを提供するベンダーもある。
21 | こういったプロジェクトが提供するパッケージは、他のパッケージや特定のフレームワークへの依存がほとんどない。
22 |
23 | たとえば[FuelPHPのValidationパッケージ][fuelval]を使うときには、
24 | 別にFuelPHPフレームワークを使う必要はない。
25 |
26 | * [Aura]
27 | * CakePHP Components
28 | * [Collection]
29 | * [Database]
30 | * [Datasource]
31 | * [Event]
32 | * [I18n]
33 | * [ORM]
34 | * [FuelPHP]
35 | * [Hoa Project]
36 | * [Symfony Components]
37 | * [The League of Extraordinary Packages]
38 | * Laravel's Illuminate Components
39 | * [IoC Container]
40 | * [Eloquent ORM]
41 | * [Queue]
42 |
43 | _Laravelの [Illuminate コンポーネント] も、将来的には Laravel フレームワークからきちんと切り離されるようになるだろう。
44 | 現段階では、Laravel フレームワークからうまく切り離せているコンポーネントだけをリストにあげておいた。_
45 |
46 |
47 | [Packagist]: /#composer_と_packagist
48 | [PEAR]: /#pear
49 | [Dependency Management]: /#依存関係の管理
50 | [FuelPHP Validation package]: https://github.com/fuelphp/validation
51 | [Aura]: https://auraphp.com/framework/
52 | [FuelPHP]: https://github.com/fuelphp
53 | [Hoa Project]: https://github.com/hoaproject
54 | [Symfony Components]: https://symfony.com/components
55 | [The League of Extraordinary Packages]: https://thephpleague.com/
56 | [IoC Container]: https://github.com/illuminate/container
57 | [Eloquent ORM]: https://github.com/illuminate/database
58 | [Queue]: https://github.com/illuminate/queue
59 | [Illuminate コンポーネント]: https://github.com/illuminate
60 | [Collection]: https://github.com/cakephp/collection
61 | [Database]: https://github.com/cakephp/database
62 | [Datasource]: https://github.com/cakephp/datasource
63 | [Event]: https://github.com/cakephp/event
64 | [I18n]: https://github.com/cakephp/i18n
65 | [ORM]: https://github.com/cakephp/orm
66 |
--------------------------------------------------------------------------------
/_posts/16-08-01-Sites.md:
--------------------------------------------------------------------------------
1 | ---
2 | isChild: true
3 | anchor: other_resources
4 | title: その他の資料
5 | ---
6 |
7 | ## その他の資料 {#other_resources_title}
8 |
9 | ### チートシート
10 |
11 | * [PHP Cheatsheets](https://phpcheatsheets.com/) - さまざまなバージョンの PHP における、変数の比較や演算など
12 | * [Modern PHP Cheatsheet](https://github.com/smknstd/modern-php-cheatsheet) - モダン(PHP 7.0+) なイディオムが、ひとつの文書にまとまっている
13 | * [OWASP Security Cheatsheets](https://owasp.org/www-project-cheat-sheets/) - セキュリティに関するさまざまなトピックの有用な情報を簡潔にまとめたもの
14 |
15 | ### ベストプラクティス
16 |
17 | * [PHP Best Practices](https://phpbestpractices.org/)
18 | * [Why You Should Be Using Supported PHP Versions](https://kinsta.com/blog/php-versions/)
19 |
20 | ### PHPやウェブ開発コミュニティ界隈のニュース
21 |
22 | ニュースレターを購読すれば、新しいライブラリや最新ニュースやイベントなどの通知を受け取れる。
23 | それだけじゃなくて、新旧さまざまな記事も紹介してくれるだろう。
24 |
25 | * [PHP Weekly](https://www.phpweekly.com)
26 | * [JavaScript Weekly](https://javascriptweekly.com/)
27 | * [Frontend Focus](https://frontendfoc.us/)
28 | * [Mobile Web Weekly](https://mobiledevweekly.com/)
29 |
30 | その他のプラットフォームにも同じような週刊ニュースレターがある。 [その一部をまとめた](https://github.com/jondot/awesome-weekly)。
31 |
32 | ### PHP界
33 |
34 | * [PHP Developer blog](https://blog.phpdeveloper.org/)
35 |
--------------------------------------------------------------------------------
/_posts/16-09-01-Videos.md:
--------------------------------------------------------------------------------
1 | ---
2 | isChild: true
3 | anchor: videos
4 | title: 動画チュートリアル
5 | ---
6 |
7 | ## 動画チュートリアル {#videos}
8 |
9 | ### YouTubeチャンネル
10 |
11 | * [Learn PHP The Right Way Series](https://github.com/ggelashvili/learnphptherightway-outline)
12 | * [PHP Academy](https://www.youtube.com/user/phpacademy)
13 | * [The New Boston](https://www.youtube.com/user/thenewboston)
14 | * [Sherif Ramadan](https://www.youtube.com/user/businessgeek)
15 | * [Level Up Tuts](https://www.youtube.com/user/LevelUpTuts)
16 |
17 | ### 有償の動画
18 |
19 | * [Standards and Best practices](https://teamtreehouse.com/library/php-standards-and-best-practices)
20 | * [PHP Training on Pluralsight](https://www.pluralsight.com/search?q=php)
21 | * [PHP Training on LinkedIn.com](https://www.linkedin.com/learning/search?trk=lynda_redirect_learning&sortBy=RELEVANCE&softwareNames=PHP)
22 | * [PHP Training on Tutsplus](https://code.tutsplus.com/categories/php/courses)
23 | * [Laracasts](https://laracasts.com/)
24 | * [SymfonyCasts](https://symfonycasts.com/)
25 |
--------------------------------------------------------------------------------
/_posts/16-10-01-Books.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 書籍
3 | isChild: true
4 | anchor: books
5 | ---
6 |
7 | ## 書籍 {#books_title}
8 |
9 | PHPに関する本はいっぱい出てるけど、絶望的に古くなってしまっていてもはや正しい情報ではなくなっているものもある。
10 | なぜか、この世に存在すらしていない「PHP 6」向けの本まで出ていたりする。
11 | これが、PHP 5.6の次のメジャーバージョンの名前が「PHP 7」に決まった[理由のひとつかもしれない](https://wiki.php.net/rfc/php6)。
12 |
13 | このセクションでは、PHPでの開発全般でおすすめの書籍を紹介する。
14 | 自分の本を載せて欲しいという人は、プルリクエストを出してもらえばレビューのうえで追加する。
15 |
16 | ### 無償の書籍
17 |
18 | * [PHP Pandas](https://daylerees.com/php-pandas/) - ウェブ開発者になるための方法を教えるのが狙いだ。
19 | * [PHP The Right Way](https://leanpub.com/phptherightway/) - このウェブサイトの内容が、無料の書籍として公開されている。
20 | * [Using Libsodium in PHP Projects](https://paragonie.com/book/pecl-libsodium) - Libsodium 拡張モジュールを使って、
21 | モダンでセキュアかつ高速な暗号化を行うためのガイド。
22 |
23 | ### 有償の書籍
24 |
25 | * [PHP & MySQL](https://phpandmysql.com/) - PHP book with excellent illustrations that covers all the fundamentals of PHP and MySQL with practical examples.
26 | * [Build APIs You Won't Hate](https://apisyouwonthate.com/) - Everyone and their dog wants an API,
27 | so you should probably learn how to build them.
28 | * [Modern PHP](https://www.oreilly.com/library/view/modern-php/9781491905173/) - Covers modern PHP features, best practices, testing, tuning, deployment and setting up a dev environment.
29 | * [Building Secure PHP Apps](https://leanpub.com/buildingsecurephpapps) - Learn the security basics that a senior
30 | developer usually acquires over years of experience, all condensed down into one quick and easy handbook.
31 | * [Modernizing Legacy Applications In PHP](https://leanpub.com/mlaphp) - 細かく順を追って、自分のコードを手なずけていく。
32 | * [Securing PHP: Core Concepts](https://leanpub.com/securingphp-coreconcepts) - A guide to some of the most common
33 | security terms and provides some examples of them in every day PHP.
34 | * [Scaling PHP](https://www.scalingphpbook.com/) - Stop playing sysadmin and get back to coding.
35 | * [Signaling PHP](https://leanpub.com/signalingphp) - PCNLT signals are a great help when writing PHP scripts that
36 | run from the command line.
37 | * [Minimum Viable Tests](https://leanpub.com/minimumviabletests) - Long-time PHP testing evangelist Chris Hartjes goes over what he feels is the minimum you need to know to get started.
38 | * [Domain-Driven Design in PHP](https://leanpub.com/ddd-in-php) - See real examples written in PHP showcasing Domain-Driven Design Architectural Styles (Hexagonal Architecture, CQRS or Event Sourcing), Tactical Design Patterns, and Bounded Context Integration.
39 |
--------------------------------------------------------------------------------
/_posts/17-01-01-Community.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: コミュニティ
3 | anchor: community
4 | ---
5 |
6 | # コミュニティ {#community_title}
7 |
8 | PHP のコミュニティは、規模が大きくなるにつれて多様化しており、
9 | どこのコミュニティでも新たな仲間を歓迎している。
10 | 近所のユーザーグループに参加したり、ちょっと大きめのカンファレンスに参加したりすれば、
11 | このページで紹介したことよりもずっと多くを学べるだろう。
12 | IRC なら [irc.freenode.com][php-irc] に #phpc というチャンネルがあるし、
13 | [@phpc][phpc-twitter] や [Mastodon][phpc-mastodon] をフォローするといい。
14 | いろんなところに飛び込んで、新しい人と出会って、いろんなことを学んで、友達になるんだ。
15 | [StackOverflow][php-so] にもPHPプログラマー向けのコミュニティがある。
16 |
17 | [PHP 公式サイトのイベントカレンダー][php-calendar]
18 |
19 |
20 | [php-irc]: https://webchat.freenode.net/?channels=phpc
21 | [phpc-twitter]: https://twitter.com/phpc
22 | [phpc-mastodon]: https://phpc.social/
23 | [php-so]: https://stackoverflow.com/questions/tagged/php
24 | [php-calendar]: https://www.php.net/cal.php
25 |
--------------------------------------------------------------------------------
/_posts/17-02-01-User-Groups.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: PHP ユーザーグループ
3 | isChild: true
4 | anchor: user_groups
5 | ---
6 |
7 | ## PHP ユーザーグループ {#user_groups_title}
8 |
9 | 大都市に住んでいるなら、きっと近場に PHP ユーザーグループがあるはずだ。
10 | 近場の PHP ユーザーグループを見つけるには、[PHP.ug][php-ug] を使えばいい。
11 | それ以外にも、[Meetup.com][meetup] を使ってもいいし、
12 | お好みのサーチエンジン (たとえば [Google][google] とか) で
13 | ```php user group near me``` みたいに検索してみるとかね。
14 | 小さな町なら、もしかしたらユーザーグループがないかもしれない。
15 | だったら、作ればいい!
16 |
17 | 数々のグループの中でも、特筆すべきものがある。[NomadPHP] と [PHPWomen] だ。
18 | [NomadPHP] は、オンラインでのミーティングを毎月2回開催している。
19 | そのミーティングでは、PHP コミュニティの有名人によるプレゼンも行われている。
20 | [PHPWomen] は、PHP 界の女性をターゲットにしたユーザーグループだったが、今はそれにとどまらない。
21 | より多様性のあるコミュニティを目指そうという人なら、誰でも受け入れている。
22 | PHPWomen は、サポートやメンターシップ、教育などのネットワークを提供している。
23 | そして「女性にやさしい」プロフェッショナルな空気を広めることを目指している。
24 |
25 | [PHP Wiki のユーザーグループ情報][php-wiki]
26 |
27 | [google]: https://www.google.com/search?q=php+user+group+near+me
28 | [meetup]: https://www.meetup.com/find/
29 | [php-ug]: https://php.ug/
30 | [NomadPHP]: https://nomadphp.com/
31 | [PHPWomen]: https://twitter.com/PHPWomen
32 | [php-wiki]: https://wiki.php.net/usergroups
33 |
--------------------------------------------------------------------------------
/_posts/17-03-01-Conferences.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: PHP カンファレンス
3 | isChild: true
4 | anchor: conferences
5 | ---
6 |
7 | ## PHP カンファレンス {#conferences_title}
8 |
9 | 世界各国で、地域レベルあるいは国レベルの大規模なカンファレンスが開かれている。
10 | PHP界の有名人が登壇することも多いので、彼らから直接学ぶチャンスだ。
11 |
12 | [PHP のカンファレンスを探す][php-conf]
13 |
14 |
15 | [php-conf]: https://www.php.net/conferences/index.php
16 |
--------------------------------------------------------------------------------
/_posts/17-04-01-Elephpants.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ElePHPants
3 | isChild: true
4 | anchor: elephpants
5 | ---
6 |
7 | ## ElePHPants {#elephpants_title}
8 |
9 | [ElePHPant][elephpant] は PHP プロジェクトのマスコットだ。
10 | もともとは 1998 年に [Vincent Pontier][vincent-pontier] がデザインしたもの。つまり彼は、世界中の elePHPant たちの父なる神である。
11 | その約10年後には、かわいらしいぬいぐるみもできあがった。
12 | いまやあちこちのPHPカンファレンスでelePHPantが見られるし、自分のPCのそばにelePHPantを置いているPHP開発者も多い。
13 |
14 | [Vincent Pontierへのインタビュー][vincent-pontier-interview]
15 |
16 |
17 | [elephpant]: https://www.php.net/elephpant.php
18 | [vincent-pontier-interview]: https://7php.com/elephpant/
19 | [vincent-pontier]: http://www.elroubio.net/
20 |
--------------------------------------------------------------------------------
/banners.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Website Banners
4 | description: "Spread the word! Use these banner to let new PHP programmers know about PHP: The Right Way"
5 | sitemap: true
6 | ---
7 |
8 | # ウェブ用のバナー
9 |
10 | このバナー画像を使って _PHP: The Right Way_ を広めよう!
11 | PHPを勉強したい人たちに「いい情報がここにあるよ!」ってぜひ伝えるんだ。
12 |
13 | ## ボタン 1 (120x90)
14 |
15 | 
16 |
17 | {% highlight html %}
18 |
19 |
20 |
21 | {% endhighlight %}
22 |
23 | ## ボタン 2 (120x60)
24 |
25 | 
26 |
27 | {% highlight html %}
28 |
29 |
30 |
31 | {% endhighlight %}
32 |
33 | ## ビッグバナー (728x90)
34 |
35 | 
36 |
37 | {% highlight html %}
38 |
39 |
40 |
41 | {% endhighlight %}
42 |
43 | ## 大型長方形 (386x280)
44 |
45 | 
46 |
47 | {% highlight html %}
48 |
49 |
50 |
51 | {% endhighlight %}
52 |
53 | ## 中型長方形 (300x250)
54 |
55 | 
56 |
57 | {% highlight html %}
58 |
59 |
60 |
61 | {% endhighlight %}
62 |
63 | ## 長方形 (180x150)
64 |
65 | 
66 |
67 | {% highlight html %}
68 |
69 |
70 |
71 | {% endhighlight %}
72 |
73 | ## 正方形 (125x125)
74 |
75 | 
76 |
77 | {% highlight html %}
78 |
79 |
80 |
81 | {% endhighlight %}
82 |
83 | ## 縦長長方形 (240x400)
84 |
85 | 
86 |
87 | {% highlight html %}
88 |
89 |
90 |
91 | {% endhighlight %}
92 |
--------------------------------------------------------------------------------
/images/banners/btn1-120x90.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/btn1-120x90.png
--------------------------------------------------------------------------------
/images/banners/btn2-120x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/btn2-120x60.png
--------------------------------------------------------------------------------
/images/banners/leaderboard-728x90.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/leaderboard-728x90.png
--------------------------------------------------------------------------------
/images/banners/lg-rect-386x280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/lg-rect-386x280.png
--------------------------------------------------------------------------------
/images/banners/med-rect-300x250.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/med-rect-300x250.png
--------------------------------------------------------------------------------
/images/banners/rect-180x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/rect-180x150.png
--------------------------------------------------------------------------------
/images/banners/sq-btn-125x125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/sq-btn-125x125.png
--------------------------------------------------------------------------------
/images/banners/vert-rect-240x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/banners/vert-rect-240x400.png
--------------------------------------------------------------------------------
/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/favicon.png
--------------------------------------------------------------------------------
/images/nmc-logo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/nmc-logo.gif
--------------------------------------------------------------------------------
/images/og-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/og-image.png
--------------------------------------------------------------------------------
/images/og-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-takagi/php-the-right-way/1a0bffe2bdc88b61d159c23b242e4254dd173655/images/og-logo.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | description: "An easy-to-read, quick reference for PHP best practices, accepted coding standards, and links to authoritative PHP tutorials around the Web"
4 | sitemap: true
5 | ---
6 |
7 | {% capture welcome_content %}{% include welcome.md %}{% endcapture %}
8 |
9 | {{ welcome_content|markdownify }}
10 |
11 |
12 | {% capture backtotop %}[Back to Top](#top){:.top}{% endcapture %}
13 | {% for post in site.posts reversed %}
14 | {% if post.isChild != true and loop.first != true %}{{ backtotop|markdownify }}
{% endif %}
15 |
16 | {{ post.content }}
17 |
18 | {% endfor %}
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "php-the-right-way",
3 | "version": "2.0.0",
4 | "devDependencies": {
5 | "grunt": "~0.4.5",
6 | "grunt-contrib-less": "~1.0.1",
7 | "grunt-contrib-watch": "~0.6.1",
8 | "grunt-postcss": "^0.6.0",
9 | "autoprefixer": "^6.0.3"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pages/Design-Patterns.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Design Patterns
4 | sitemap: true
5 | ---
6 |
7 | # デザインパターン
8 |
9 | ウェブアプリケーションのコードやプロジェクトを作っていくにはいろんなやりかたがあって、
10 | どんなふうに作るか考え抜くのもありだし適当に作るのもありだ。
11 | でも普通は、一般的なパターンに従うほうがいい。そのほうがコードを管理しやすいし、
12 | 他の人にもそのコードを理解してもらいやすくなるからである。
13 |
14 | * [Architectural pattern (Wikipedia)](https://en.wikipedia.org/wiki/Architectural_pattern)
15 | * [デザインパターン (Wikipedia)](https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3_(%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2))
16 | * [実装サンプル集](https://designpatternsphp.readthedocs.io/en/latest/)
17 |
18 | ## ファクトリー
19 |
20 | 最も多用されているデザインパターンのひとつが、ファクトリーパターンだ。このパターンでは、
21 | 使いたいオブジェクトを生成するだけのクラスを用意する。ファクトリーパターンの例として、
22 | こんなコードを考えてみよう。
23 |
24 | {% highlight php %}
25 | vehicleMake = $make;
34 | $this->vehicleModel = $model;
35 | }
36 |
37 | public function getMakeAndModel()
38 | {
39 | return $this->vehicleMake . ' ' . $this->vehicleModel;
40 | }
41 | }
42 |
43 | class AutomobileFactory
44 | {
45 | public static function create($make, $model)
46 | {
47 | return new Automobile($make, $model);
48 | }
49 | }
50 |
51 | // ファクトリーを使って Automobile オブジェクトを作る
52 | $veyron = AutomobileFactory::create('ブガッティ', 'ヴェイロン');
53 |
54 | print_r($veyron->getMakeAndModel()); // 出力は "ブガッティ ヴェイロン"
55 | {% endhighlight %}
56 |
57 | このコードは、ファクトリーパターンを使って Automobile オブジェクトを作る。
58 | こんなふうにするメリットは二つある。
59 | まず、もし後で Automobile クラスに手を入れたり名前を変更したり別のものに入れ替えたりすることになっても簡単にできるということ。
60 | 単にファクトリーの中のコードを変更すれば済むわけで、
61 | コードの中で Automobile クラスを使っているところをひとつひとつ修正するとかいうことはしなくてもよい。
62 | 二番目のメリットは、仮にオブジェクトの生成が複雑な作業になってしまっても、
63 | そのすべてのファクトリーに閉じ込めてしまえること。
64 | 新しいインスタンスを作るたびに毎回同じようなことを繰り返さずに済む。
65 |
66 | なにがなんでもファクトリーパターンを使えばいいってわけではない。
67 | この例のコードはとてもシンプルだし、この程度だとファクトリーパターンのおかげで
68 | 無駄に複雑になったように見えるかもしれない。
69 | でも、それなりに大規模で込み入ったプロジェクトにかかわる場合は、
70 | 変なトラブルに巻き込まれないためにもファクトリーを使っておいたほうがいいだろう。
71 |
72 | * [ファクトリーパターン (Wikipedia)](https://ja.wikipedia.org/wiki/Factory_Method_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)
73 |
74 | ## シングルトン
75 |
76 | ウェブアプリケーションを設計するときには、概念的そして構造的に、
77 | 特定のクラスのたったひとつのインスタンスにだけアクセスさせるようにしたいということがよくある。
78 | そんなときに使えるのがシングルトンパターンだ。
79 |
80 | **TODO: NEED NEW SINGLETON CODE EXAMPLE**
81 |
82 | このコードは、[*静的* な変数](https://www.php.net/language.variables.scope#language.variables.scope.static)
83 | と`getInstance()`メソッドを使ってシングルトンパターンを実装している。
84 | これらのことに注目しよう。
85 |
86 | * コンストラクタ [`__construct`](https://www.php.net/language.oop5.decon#object.construct) が protected 宣言されている。これで、このクラスの外部からは `new` 演算子で新しいインスタンスを作れなくなる。
87 | * マジックメソッド [`__clone`](https://www.php.net/language.oop5.cloning#object.clone) が private 宣言されている。これで、[`clone`](http://php.net/language.oop5.cloning) 演算子でインスタンスをクローンしようとしてもできなくなる。
88 | * マジックメソッド [`__wakeup`](https://www.php.net/language.oop5.magic#object.wakeup) が private 宣言されている。これで、グローバル関数 [`unserialize()`](https://www.php.net/function.unserialize) でのインスタンスのアンシリアライズができなくなる。
89 | * 新しいインスタンスを作るには、静的な作成用メソッド `getInstance()` による [遅延静的束縛](https://www.php.net/language.oop5.late-static-bindings) を使う。このメソッドの中ではキーワード `static` が使われていて、サンプルのように `Singleton` のサブクラスを作れるようになる。
90 |
91 | シングルトンパターンが有用なのは、たとえばウェブアプリケーションのリクエスト全体で、
92 | たったひとつのインスタンスだけしかないことを保証しないといけない場合だ。
93 | Configurationクラスみたいなグローバルオブジェクトを使っていたり、
94 | イベントキューみたいな共有リソースを使っていたりする場合に活用できる。
95 |
96 | ただ、シングルトンを使うときには注意が必要だ。その性質上、シングルトンパターンを使うと
97 | アプリケーションにグローバルな状態を導入することになってしまい、テストがしにくくなる。
98 | シングルトンを使いたいという場面では、たいていの場合は依存性の注入が代わりに使える
99 | (し、むしろそっちを使うべきだ)。依存性の注入を使えば、不要な結合を
100 | アプリケーションの設計から取り除ける。というのも、共有リソースやグローバルリソースを使う
101 | オブジェクトが具象クラスについて知らなくてもよくなるからだ。
102 |
103 | * [シングルトンパターン (Wikipedia)](https://en.wikipedia.org/wiki/Singleton_pattern)
104 |
105 | ## ストラテジー
106 |
107 | ストラテジーパターンを使うと、一連のアルゴリズム群をカプセル化して、
108 | クライアントクラス側から特定のアルゴリズムのインスタンスを作れるようになる。
109 | このときに、実際の実装に関する知識はなくてもかまわない。
110 | このパターンにはいくつかのバリエーションがあるけれど、ここでは一番シンプルなものを解説する。
111 |
112 | 最初に示すのは、アルゴリズム群を表すコード片だ。
113 | シリアライズした配列、JSON、あるいは単なるデータの配列が欲しいとしよう。
114 | {% highlight php %}
115 | output = $outputType;
168 | }
169 |
170 | public function loadOutput()
171 | {
172 | return $this->output->load();
173 | }
174 | }
175 | {% endhighlight %}
176 |
177 | このクライアントクラスにはprivateなプロパティが用意されている。
178 | 実行時には、必ず'OutputInterface'型を設定しておく必要がある。
179 | このプロパティを設定すれば、loadOutput()を呼んだときに、
180 | 指定した型の具象クラスのload()メソッドが呼ばれることになる。
181 | {% highlight php %}
182 | setOutput(new ArrayOutput());
187 | $data = $client->loadOutput();
188 |
189 | // JSONが使いたければ
190 | $client->setOutput(new JsonStringOutput());
191 | $data = $client->loadOutput();
192 |
193 | {% endhighlight %}
194 |
195 | * [ストラテジーパターン (Wikipedia)](https://en.wikipedia.org/wiki/Strategy_pattern)
196 |
197 | ## フロントコントローラ
198 |
199 | フロントコントローラパターンは、ウェブアプリケーションのエントリポイントをひとつだけ (例: index.php) にして
200 | そこですべてのリクエストを処理するというパターンだ。このエントリポイントが、
201 | 依存情報を読み込んだりリクエストを処理したりレスポンスをブラウザに返したりといった責務を負う。
202 | フロントコントローラを利用すると、コードのモジュール化が進めやすくなる。
203 | また、すべてのリクエストに対して実行したいコード
204 | (入力のチェックなど) をフックとして組み込みやすくなる。
205 |
206 | * [フロントコントローラパターン (Wikipedia)](https://en.wikipedia.org/wiki/Front_Controller_pattern)
207 |
208 | ## Model-View-Controller
209 |
210 | モデル=ビュー=コントローラ (MVC) パターン、そしてその関連パターンである HMVC や MVVM
211 | を使うと、コードを論理的に分解してそれぞれ特定の役割を担わせることができる。
212 | モデルはデータアクセス層を扱い、データを取得してそれをアプリケーションで使いやすい形式で返す。
213 | コントローラはリクエストを扱い、モデルから受け取ったデータを処理してビューを読み込み、
214 | それをレスポンスとして返す。
215 | ビューはテンプレート (マークアップや xml など) を扱い、これをレスポンスとしてウェブブラウザに返す。
216 |
217 | MVC は最も一般的なアーキテクチャパターンで、主要な [PHP フレームワーク](https://github.com/codeguy/php-the-right-way/wiki/Frameworks)
218 | でも採用されている。
219 |
220 | MVC やその関連パターンについてさらに知りたければ、これらを参考にしよう。
221 |
222 | * [MVC](https://ja.wikipedia.org/wiki/Model_View_Controller)
223 | * [HMVC](https://en.wikipedia.org/wiki/Hierarchical_model%E2%80%93view%E2%80%93controller)
224 | * [MVVM](https://ja.wikipedia.org/wiki/Model_View_ViewModel)
225 |
--------------------------------------------------------------------------------
/pages/Functional-Programming.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Functional Programming in PHP
4 | sitemap: true
5 | ---
6 |
7 | # PHP における関数型プログラミング
8 |
9 | PHP は、ファーストクラスの関数をサポートしている。
10 | つまり、関数を変数に代入できるってことだ。
11 | 自分で定義した関数だろうがもともと組み込まれている関数だろうが、
12 | 変数で参照したり動的に実行したりできる。
13 | 何かの関数を別の関数の引数として渡すこともできるし、関数の返り値を別の関数にすることもできる(高階関数っていう機能)。
14 |
15 | 再帰 (ある関数の中から自分自身を呼ぶこと) も PHP の機能としてサポートしている。
16 | しかし、たいていの PHP コードはそれよりも逐次処理を重視している。
17 |
18 | 新型の無名関数 (クロージャにも対応したもの) が使えるようになったのは、PHP 5.3 (2009 年) 以降だ。
19 |
20 | PHP 5.4 からは、クロージャをオブジェクトのスコープにバインドできるようになった。
21 | また callable のサポートも強化され、ほとんどの場合で無名関数と互換性を持つようになった。
22 |
23 | 高階関数の最もよくある使いかたは、ストラテジーパターンの実装だ。
24 | 組み込みの `array_filter` 関数は、入力の配列 (データ)
25 | と関数 (ストラテジーあるいはコールバック) を受け取って、
26 | 配列の各要素に対してその関数をフィルターとして適用する。
27 |
28 | {% highlight php %}
29 | $min を通すもの) を作る
59 | *
60 | * 「n より大きい」フィルター群の中からひとつのフィルターを返す
61 | */
62 | function criteria_greater_than($min)
63 | {
64 | return function($item) use ($min) {
65 | return $item > $min;
66 | };
67 | }
68 |
69 | $input = array(1, 2, 3, 4, 5, 6);
70 |
71 | // array_filter を使い、入力値とフィルター関数を指定する
72 | $output = array_filter($input, criteria_greater_than(3));
73 |
74 | print_r($output); // items > 3
75 | {% endhighlight %}
76 |
77 | フィルター関数群の各関数は、何らかの最小値を上回る要素だけを通過させる。
78 | `criteria_greater_than` が返すフィルターはクロージャであり、
79 | `$min` 引数の値 (`criteria_greater_than` の呼び出し時に引数として渡したもの)
80 | がスコープ内で束縛されている。
81 |
82 | デフォルトでは、`$min` 変数の値を関数にインポートするときに早期束縛を使う。
83 | 遅延束縛を使った真のクロージャを使いたい場合は、インポートの際に参照を使わないといけない。
84 | テンプレート、あるいは入力バリデーションライブラリを考えてみよう。
85 | こんな場合、クロージャを定義してスコープ内の変数を捕捉しておいて、
86 | あとで無名関数が評価されたときにそれにアクセスすることになる。
87 |
88 | * [無名関数][anonymous-functions]
89 | * [クロージャの詳細を知りたければ、RFCを読めばいいよ][closures-rfc]
90 | * [`call_user_func_array()`による動的な関数実行][call-user-func-array]
91 |
92 |
93 | [anonymous-functions]: https://www.php.net/functions.anonymous
94 | [closures-rfc]: https://wiki.php.net/rfc/closures
95 | [call-user-func-array]: https://www.php.net/function.call-user-func-array
96 |
--------------------------------------------------------------------------------
/pages/example.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Example Stand-Alone Page
4 | ---
5 |
6 | # Page Title
7 |
8 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
9 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
10 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
11 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
12 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
13 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
14 |
15 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
16 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
17 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
18 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
19 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
20 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
21 |
22 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
23 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
24 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
25 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
26 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
27 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
28 |
--------------------------------------------------------------------------------
/scripts/fastclick.js:
--------------------------------------------------------------------------------
1 | /** Shrinkwrap URL:
2 | * /v2/bundles/js?modules=fastclick%401.0.6%2Co-autoinit%401.0.1&shrinkwrap=
3 | */
4 | !function(t){function e(o){if(n[o])return n[o].exports;var i=n[o]={exports:{},id:o,loaded:!1};return t[o].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";n(1),window.Origami={fastclick:n(2),"o-autoinit":n(4)}},function(t,e){t.exports={name:"__MAIN__",dependencies:{fastclick:"fastclick#*","o-autoinit":"o-autoinit#^1.0.0"}}},function(t,e,n){t.exports=n(3)},function(t,e){"use strict";var n=!1;!function(){function e(t,n){function o(t,e){return function(){return t.apply(e,arguments)}}var r;if(n=n||{},this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.touchBoundary=n.touchBoundary||10,this.layer=t,this.tapDelay=n.tapDelay||200,this.tapTimeout=n.tapTimeout||700,!e.notNeeded(t)){for(var a=["onMouse","onClick","onTouchStart","onTouchMove","onTouchEnd","onTouchCancel"],c=this,s=0,u=a.length;u>s;s++)c[a[s]]=o(c[a[s]],c);i&&(t.addEventListener("mouseover",this.onMouse,!0),t.addEventListener("mousedown",this.onMouse,!0),t.addEventListener("mouseup",this.onMouse,!0)),t.addEventListener("click",this.onClick,!0),t.addEventListener("touchstart",this.onTouchStart,!1),t.addEventListener("touchmove",this.onTouchMove,!1),t.addEventListener("touchend",this.onTouchEnd,!1),t.addEventListener("touchcancel",this.onTouchCancel,!1),Event.prototype.stopImmediatePropagation||(t.removeEventListener=function(e,n,o){var i=Node.prototype.removeEventListener;"click"===e?i.call(t,e,n.hijacked||n,o):i.call(t,e,n,o)},t.addEventListener=function(e,n,o){var i=Node.prototype.addEventListener;"click"===e?i.call(t,e,n.hijacked||(n.hijacked=function(t){t.propagationStopped||n(t)}),o):i.call(t,e,n,o)}),"function"==typeof t.onclick&&(r=t.onclick,t.addEventListener("click",function(t){r(t)},!1),t.onclick=null)}}var o=navigator.userAgent.indexOf("Windows Phone")>=0,i=navigator.userAgent.indexOf("Android")>0&&!o,r=/iP(ad|hone|od)/.test(navigator.userAgent)&&!o,a=r&&/OS 4_\d(_\d)?/.test(navigator.userAgent),c=r&&/OS [6-7]_\d/.test(navigator.userAgent),s=navigator.userAgent.indexOf("BB10")>0;e.prototype.needsClick=function(t){switch(t.nodeName.toLowerCase()){case"button":case"select":case"textarea":if(t.disabled)return!0;break;case"input":if(r&&"file"===t.type||t.disabled)return!0;break;case"label":case"iframe":case"video":return!0}return/\bneedsclick\b/.test(t.className)},e.prototype.needsFocus=function(t){switch(t.nodeName.toLowerCase()){case"textarea":return!0;case"select":return!i;case"input":switch(t.type){case"button":case"checkbox":case"file":case"image":case"radio":case"submit":return!1}return!t.disabled&&!t.readOnly;default:return/\bneedsfocus\b/.test(t.className)}},e.prototype.sendClick=function(t,e){var n,o;document.activeElement&&document.activeElement!==t&&document.activeElement.blur(),o=e.changedTouches[0],n=document.createEvent("MouseEvents"),n.initMouseEvent(this.determineEventType(t),!0,!0,window,1,o.screenX,o.screenY,o.clientX,o.clientY,!1,!1,!1,!1,0,null),n.forwardedTouchEvent=!0,t.dispatchEvent(n)},e.prototype.determineEventType=function(t){return i&&"select"===t.tagName.toLowerCase()?"mousedown":"click"},e.prototype.focus=function(t){var e;r&&t.setSelectionRange&&0!==t.type.indexOf("date")&&"time"!==t.type&&"month"!==t.type?(e=t.value.length,t.setSelectionRange(e,e)):t.focus()},e.prototype.updateScrollParent=function(t){var e,n;if(e=t.fastClickScrollParent,!e||!e.contains(t)){n=t;do{if(n.scrollHeight>n.offsetHeight){e=n,t.fastClickScrollParent=n;break}n=n.parentElement}while(n)}e&&(e.fastClickLastScrollTop=e.scrollTop)},e.prototype.getTargetElementFromEventTarget=function(t){return t.nodeType===Node.TEXT_NODE?t.parentNode:t},e.prototype.onTouchStart=function(t){var e,n,o;if(t.targetTouches.length>1)return!0;if(e=this.getTargetElementFromEventTarget(t.target),n=t.targetTouches[0],r){if(o=window.getSelection(),o.rangeCount&&!o.isCollapsed)return!0;if(!a){if(n.identifier&&n.identifier===this.lastTouchIdentifier)return t.preventDefault(),!1;this.lastTouchIdentifier=n.identifier,this.updateScrollParent(e)}}return this.trackingClick=!0,this.trackingClickStart=t.timeStamp,this.targetElement=e,this.touchStartX=n.pageX,this.touchStartY=n.pageY,t.timeStamp-this.lastClickTimen||Math.abs(e.pageY-this.touchStartY)>n?!0:!1},e.prototype.onTouchMove=function(t){return this.trackingClick?((this.targetElement!==this.getTargetElementFromEventTarget(t.target)||this.touchHasMoved(t))&&(this.trackingClick=!1,this.targetElement=null),!0):!0},e.prototype.findControl=function(t){return void 0!==t.control?t.control:t.htmlFor?document.getElementById(t.htmlFor):t.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")},e.prototype.onTouchEnd=function(t){var e,n,o,s,u,l=this.targetElement;if(!this.trackingClick)return!0;if(t.timeStamp-this.lastClickTimethis.tapTimeout)return!0;if(this.cancelNextClick=!1,this.lastClickTime=t.timeStamp,n=this.trackingClickStart,this.trackingClick=!1,this.trackingClickStart=0,c&&(u=t.changedTouches[0],l=document.elementFromPoint(u.pageX-window.pageXOffset,u.pageY-window.pageYOffset)||l,l.fastClickScrollParent=this.targetElement.fastClickScrollParent),o=l.tagName.toLowerCase(),"label"===o){if(e=this.findControl(l)){if(this.focus(l),i)return!1;l=e}}else if(this.needsFocus(l))return t.timeStamp-n>100||r&&window.top!==window&&"input"===o?(this.targetElement=null,!1):(this.focus(l),this.sendClick(l,t),r&&"select"===o||(this.targetElement=null,t.preventDefault()),!1);return r&&!a&&(s=l.fastClickScrollParent,s&&s.fastClickLastScrollTop!==s.scrollTop)?!0:(this.needsClick(l)||(t.preventDefault(),this.sendClick(l,t)),!1)},e.prototype.onTouchCancel=function(){this.trackingClick=!1,this.targetElement=null},e.prototype.onMouse=function(t){return this.targetElement?t.forwardedTouchEvent?!0:t.cancelable&&(!this.needsClick(this.targetElement)||this.cancelNextClick)?(t.stopImmediatePropagation?t.stopImmediatePropagation():t.propagationStopped=!0,t.stopPropagation(),t.preventDefault(),!1):!0:!0},e.prototype.onClick=function(t){var e;return this.trackingClick?(this.targetElement=null,this.trackingClick=!1,!0):"submit"===t.target.type&&0===t.detail?!0:(e=this.onMouse(t),e||(this.targetElement=null),e)},e.prototype.destroy=function(){var t=this.layer;i&&(t.removeEventListener("mouseover",this.onMouse,!0),t.removeEventListener("mousedown",this.onMouse,!0),t.removeEventListener("mouseup",this.onMouse,!0)),t.removeEventListener("click",this.onClick,!0),t.removeEventListener("touchstart",this.onTouchStart,!1),t.removeEventListener("touchmove",this.onTouchMove,!1),t.removeEventListener("touchend",this.onTouchEnd,!1),t.removeEventListener("touchcancel",this.onTouchCancel,!1)},e.notNeeded=function(t){var e,n,o,r;if("undefined"==typeof window.ontouchstart)return!0;if(n=+(/Chrome\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]){if(!i)return!0;if(e=document.querySelector("meta[name=viewport]")){if(-1!==e.content.indexOf("user-scalable=no"))return!0;if(n>31&&document.documentElement.scrollWidth<=window.outerWidth)return!0}}if(s&&(o=navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/),o[1]>=10&&o[2]>=3&&(e=document.querySelector("meta[name=viewport]")))){if(-1!==e.content.indexOf("user-scalable=no"))return!0;if(document.documentElement.scrollWidth<=window.outerWidth)return!0}return"none"===t.style.msTouchAction||"manipulation"===t.style.touchAction?!0:(r=+(/Firefox\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1],r>=27&&(e=document.querySelector("meta[name=viewport]"),e&&(-1!==e.content.indexOf("user-scalable=no")||document.documentElement.scrollWidth<=window.outerWidth))?!0:"none"===t.style.touchAction||"manipulation"===t.style.touchAction?!0:!1)},e.attach=function(t,n){return new e(t,n)},"function"==typeof n&&"object"==typeof n.amd&&n.amd?n(function(){return e}):"undefined"!=typeof t&&t.exports?(t.exports=e.attach,t.exports.FastClick=e):window.FastClick=e}()},function(t,e,n){t.exports=n(5)},function(t,e){"use strict";function n(t){t in o||(o[t]=!0,document.dispatchEvent(new CustomEvent("o."+t)))}var o={};window.addEventListener("load",n.bind(null,"load")),window.addEventListener("load",n.bind(null,"DOMContentLoaded")),document.addEventListener("DOMContentLoaded",n.bind(null,"DOMContentLoaded")),"complete"===document.readyState?(n("load"),n("DOMContentLoaded")):"interactive"===document.readyState&&n("DOMContentLoaded")}]);
5 |
--------------------------------------------------------------------------------
/scripts/setup.js:
--------------------------------------------------------------------------------
1 | (function ($) {
2 | // Attach FastClick
3 | var attachFastClick = Origami.fastclick;
4 | attachFastClick(document.body);
5 |
6 | // Mobile TOC menu
7 | var $window = $(window),
8 | $nav = $('.site-navigation');
9 | $nav.click(function (e) {
10 | var $target = $(e.target);
11 | if ($target.is($nav) && $window.width() <= 375) {
12 | $nav.toggleClass('open');
13 | }
14 | if ($target.is('a')) {
15 | $nav.removeClass('open');
16 | }
17 | });
18 | })(jQuery);
19 |
--------------------------------------------------------------------------------
/styles/all.less:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | NMC Bootstrap
3 |
4 | This LESS file imports all other LESS files. You should compile
5 | and minify this file before site launch.
6 | ========================================================================== */
7 |
8 | /* Import NMC bootstrap */
9 |
10 | @import "base/all";
11 |
12 | /* Import site-specific styles */
13 |
14 | @import "site/site-header.less";
15 | @import "site/site-navigation.less";
16 | @import "site/site-content.less";
17 | @import "site/site-footer.less";
18 |
19 | /* Tablets and Smartphones */
20 |
21 | @media only screen and (max-width : 1024px) {
22 | .build-date{
23 | text-align: center;
24 | }
25 | .site-header{
26 | height: 220px;
27 | position: absolute;
28 | top: 20px;
29 | left: 0;
30 | width: 100%;
31 | }
32 | .fork-me img{
33 | height: 110px;
34 | width: 110px;
35 | }
36 | .site-navigation{
37 | margin-top: 240px;
38 | padding: 20px;
39 | position: relative;
40 | width: auto;
41 |
42 | ul{
43 | border: 1px solid #999;
44 | border-bottom: none;
45 | }
46 | li{
47 | .man;
48 | .pan;
49 | }
50 | a{
51 | background: #CCC;
52 | display: block;
53 | border-bottom: 1px solid #999;
54 | padding: 10px;
55 | text-decoration: none;
56 | }
57 | ul ul{
58 | border: none;
59 | .man;
60 | .pan;
61 | }
62 | ul ul a{
63 | background: transparent;
64 | }
65 | }
66 | .site-content{
67 | padding: 20px;
68 | }
69 | .top{
70 | display: inline-block;
71 | float: none;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/styles/base/all.less:
--------------------------------------------------------------------------------
1 | @import "reset";
2 | @import "prefixer";
3 | @import "spacing";
4 | @import "typography";
5 | @import "idioms";
6 | @import "grid";
7 | @import "bars-buttons";
8 | @import "buttons";
--------------------------------------------------------------------------------
/styles/base/bars-buttons.less:
--------------------------------------------------------------------------------
1 | .button() {
2 | .border-box;
3 | cursor: pointer;
4 | display: inline-block;
5 | .phh;
6 | text-align: center;
7 | font-weight: bold;
8 | }
9 | .button-hover(){
10 | text-decoration: none;
11 | }
12 | .bar() {
13 | display: block;
14 | .pah;
15 | text-align: center;
16 | font-weight: bold;
17 | }
18 |
19 | /* Sizes */
20 |
21 | .btn-size(@scale){
22 | height: @baseline * @scale !important;
23 | padding-bottom: 0 !important;
24 | padding-top: 0 !important;
25 | line-height: @baseline * (@scale * 0.9);
26 | }
27 | .btn-half{
28 | .btn-size(1);
29 | font-size: 0.8em;
30 | }
31 | .btn-single{
32 | .btn-size(1.5);
33 | font-size: 1em;
34 | }
35 | .btn-double{
36 | .btn-size(2);
37 | font-size: 1.1em;
38 | }
39 |
40 | /* Shapes */
41 |
42 | .bb-shape-square() {
43 | .border-radius(0);
44 | }
45 | .bb-shape-rounded(@rad:3px) {
46 | .border-radius(@rad);
47 | }
48 | .bb-shape-round() {
49 | .border-radius(@baseline);
50 | }
51 |
52 | /* Text */
53 |
54 | .bb-text-dark(){
55 | color: #333;
56 | text-shadow: 0 1px 0 #fff;
57 | }
58 | .bb-text-light(){
59 | color: #fff;
60 | text-shadow: 0 -1px 0 rgba(0,0,0,.3);
61 | }
62 | .bb-text-color(@color){
63 |
64 | }
65 |
66 | /* Color */
67 |
68 | .bb-color-plain(@color){
69 | background: @color;
70 | }
71 | .bb-color-gradient(@color){
72 | .gradient(@color,lighten(@color,10%),darken(@color,10%));
73 | }
74 | .bb-color-soft(@color){
75 | .gradient(@color,lighten(@color,5%),darken(@color,5%));
76 | }
77 | .bb-color-gloss(@color){
78 | @topStart: desaturate(lighten(@color,40%),20%);
79 | @topStop: desaturate(lighten(@color,20%),40%);
80 | @bottomStart: desaturate(lighten(@color,10%),30%);
81 | @bottomStop: desaturate(lighten(@color,15%),30%);
82 | .linear-gradient-top(@color,@topStart,0%,@topStop,50%,@bottomStart,50%,@bottomStop,100%);
83 | }
84 |
85 | /* Border */
86 |
87 | .bb-border-noborder(){
88 | border: none;
89 | }
90 | .bb-border-plain(@color){
91 | border: 1px solid darken(@color,10%);
92 | }
93 | .bb-border-contrast(@color){
94 | border: 1px solid darken(@color,15%);
95 | .box-shadow(inset 0 0 1px 1px lighten(@color,15%));
96 | }
97 | .bb-border-meta(@color){
98 | border: 1px solid darken(@color,15%);
99 | .box-shadow(inset 0 2px 1px -1px lighten(@color,20%));
100 | }
101 |
102 | /* Minimal */
103 |
104 | .button-minimal(@color) {
105 | .button();
106 | .bb-shape-rounded();
107 | .bb-color-plain(@color);
108 | .bb-border-contrast(@color);
109 | .bb-text-dark();
110 | }
111 | .button-minimal-hover(@color){
112 | .button-minimal(darken(@color,5%));
113 | .button-hover();
114 | }
115 | .button-minimal-active(@color){
116 | .button-minimal-hover(darken(@color,5%));
117 | }
118 | .bar-minimal(@color) {
119 | .button-minimal(@color);
120 | .bar();
121 | }
122 |
123 | /* Clean */
124 |
125 | .button-clean(@color) {
126 | .button();
127 | .bb-shape-rounded();
128 | .bb-color-gradient(@color);
129 | .bb-border-plain(darken(@color,5%));
130 | .bb-text-dark();
131 | }
132 | .button-clean-hover(@color){
133 | .button-clean(darken(@color,5%));
134 | .button-hover();
135 | }
136 | .button-clean-active(@color){
137 | .button-clean-hover(darken(@color,5%));
138 | }
139 | .bar-clean(@color){
140 | .button-clean(@color);
141 | .bar();
142 | }
143 |
144 | /* Soft */
145 |
146 | .button-soft(@color) {
147 | .button();
148 | .bb-shape-rounded();
149 | .bb-color-soft(@color);
150 | .bb-border-meta(darken(@color,5%));
151 | .bb-text-light();
152 | }
153 | .button-soft-hover(@color){
154 | .button-soft(darken(@color,5%));
155 | .button-hover();
156 | }
157 | .button-soft-active(@color){
158 | .button-soft-hover(darken(@color,5%));
159 | }
160 | .bar-soft(@color){
161 | .button-soft(@color);
162 | .bar();
163 | }
164 |
165 | /* Pill */
166 |
167 | .button-pill(@color) {
168 | .button();
169 | .bb-shape-round();
170 | .bb-color-soft(@color);
171 | .bb-border-meta(darken(@color,5%));
172 | .bb-text-light();
173 | }
174 | .button-pill-hover(@color){
175 | .button-pill(darken(@color,5%));
176 | .button-hover();
177 | }
178 | .button-pill-active(@color){
179 | .button-pill-hover(darken(@color,5%));
180 | }
181 | .bar-pill(@color){
182 | .button-pill(@color);
183 | .bar();
184 | }
185 |
186 | /* Gloss */
187 |
188 | .button-gloss(@color) {
189 | .button();
190 | .bb-shape-rounded(5px);
191 | .bb-color-gloss(@color);
192 | .bb-border-plain(darken(@color,5%));
193 | .box-shadow(inset 0 1px 0 0 rgba(255,255,255,.5));
194 | .bb-text-light();
195 | }
196 | .button-gloss-hover(@color){
197 | .button-gloss(darken(@color,5%));
198 | .box-shadow(inset 0 1px 0 0 rgba(255,255,255,.3));
199 | .button-hover();
200 | }
201 | .button-gloss-active(@color){
202 | .button-gloss-hover(darken(@color,5%));
203 | .box-shadow(inset 0 0 5px 0 rgba(0,0,0,.3));
204 | }
205 | .bar-gloss(@color){
206 | .button-gloss(@color);
207 | .bar();
208 | }
209 |
210 | @btn-minimal-color: #eee;
211 | .btn-minimal { .button-minimal(@btn-minimal-color); }
212 | .btn-minimal:hover { .button-minimal-hover(@btn-minimal-color); }
213 | .btn-minimal:active { .button-minimal-active(@btn-minimal-color); }
214 |
215 | @btn-clean-color: #eee;
216 | .btn-clean { .button-clean(@btn-clean-color); }
217 | .btn-clean:hover { .button-clean-hover(@btn-clean-color); }
218 | .btn-clean:active { .button-clean-active(@btn-clean-color); }
219 |
220 | @btn-soft-color: #6C84AB;
221 | .btn-soft { .button-soft(@btn-soft-color); }
222 | .btn-soft:hover { .button-soft-hover(@btn-soft-color); }
223 | .btn-soft:active { .button-soft-active(@btn-soft-color); }
224 |
225 | @btn-pill-color: #6C84AB;
226 | .btn-pill { .button-pill(@btn-pill-color); }
227 | .btn-pill:hover { .button-pill-hover(@btn-pill-color); }
228 | .btn-pill:active { .button-pill-active(@btn-pill-color); }
229 |
230 | @btn-gloss-color: #172D6E;
231 | .btn-gloss { .button-gloss(@btn-gloss-color); }
232 | .btn-gloss:hover { .button-gloss-hover(@btn-gloss-color); }
233 | .btn-gloss:active { .button-gloss-active(@btn-gloss-color); }
234 |
235 |
236 | .bar-minimal { .bar-minimal(@btn-minimal-color); }
237 | .bar-clean { .bar-clean(@btn-clean-color); }
238 | .bar-soft { .bar-soft(@btn-soft-color); }
239 | .bar-pill { .bar-pill(@btn-pill-color); }
240 | .bar-gloss { .bar-gloss(@btn-gloss-color); }
--------------------------------------------------------------------------------
/styles/base/buttons.less:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | Settings
3 | ========================================================================== */
4 |
5 | @import 'variables.less';
6 |
7 | /*
8 | @baseline: @baseline;
9 | @button-color: @button-color;
10 | @button-primary-color: @button-primary-color;
11 | @button-info-color: @button-info-color;
12 | @button-success-color: @button-success-color;
13 | @button-warning-color: @button-warning-color;
14 | @button-danger-color: @button-danger-color;
15 | */
16 |
17 | /* ==========================================================================
18 | Default
19 | ========================================================================== */
20 |
21 | .btn{
22 | .btn-s;
23 | background-clip: border-box !important;
24 | background-repeat: repeat-x;
25 | border: 1px solid rgba(0, 0, 0, 0.25);
26 | .border-box;
27 | .border-radius(4px);
28 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
29 | cursor: pointer;
30 | display: inline-block;
31 | .gradient(@button-color, lighten(@button-color, 10%), @button-color);
32 | .phh;
33 | .pvn;
34 | .transition(background-position linear 0.1s);
35 | vertical-align: middle;
36 | color: #333;
37 | font-family: @body-font-family;
38 | text-decoration: none !important;
39 | text-shadow: rgba(255,255,255,0.75) 0 1px 0;
40 |
41 | &:hover{
42 | background-position: 0 -15px;
43 | }
44 | }
45 |
46 | /* ==========================================================================
47 | Styles
48 | ========================================================================== */
49 |
50 | .btn-primary,
51 | .btn-info,
52 | .btn-success,
53 | .btn-warning,
54 | .btn-danger{
55 | color: #FFF !important;
56 | text-shadow: rgba(0,0,0,0.25) 0 -1px 0 !important;
57 | }
58 | .btn-primary{
59 | .btn;
60 | .gradient(@button-primary-color, lighten(@button-primary-color, 10%), @button-primary-color);
61 | }
62 | .btn-info{
63 | .btn;
64 | .gradient(@button-info-color, lighten(@button-info-color, 10%), @button-info-color);
65 | }
66 | .btn-success{
67 | .btn;
68 | .gradient(@button-success-color, lighten(@button-success-color, 10%), @button-success-color);
69 | }
70 | .btn-warning{
71 | .btn;
72 | .gradient(@button-warning-color, lighten(@button-warning-color, 10%), @button-warning-color);
73 | }
74 | .btn-danger{
75 | .btn;
76 | .gradient(@button-danger-color, lighten(@button-danger-color, 10%), @button-danger-color);
77 | }
78 |
79 | /* ==========================================================================
80 | Sizes (Half = h, Single = s, Double = d)
81 | ========================================================================== */
82 |
83 | .btn-h, .btn-half{
84 | height: @baseline;
85 | font-size: @baseline * 0.6;
86 | line-height: @baseline;
87 | }
88 | .btn-s, .btn-single{
89 | height: @baseline * 1.5;
90 | font-size: @baseline * 0.75;
91 | line-height: @baseline * 1.5;
92 | }
93 | .btn-d, .btn-double{
94 | height: @baseline * 2;
95 | font-size: @baseline;
96 | line-height: @baseline * 2;
97 | }
--------------------------------------------------------------------------------
/styles/base/grid.less:
--------------------------------------------------------------------------------
1 | /**
2 | * Hybrid Grid Sytem
3 | *
4 | * Blend of the Semantic Grid System and Zurb Foundation with a little Twitter Bootstrap
5 | */
6 |
7 | /* Settings */
8 |
9 | @import 'variables.less';
10 |
11 | /*
12 | @fixed-column-width: 40px;
13 | @fixed-gutter-width: 20px;
14 | @fixed-columns: 12;
15 |
16 | @fluid-column-width: 4.3%;
17 | @fluid-gutter-width: 4.4%;
18 | @fluid-columns: 12;
19 |
20 | @mobile-break-width: 480px;
21 | @mobile-column-width: 8.6%;
22 | @mobile-gutter-width: 8.8%;
23 | @mobile-columns: 6;
24 | */
25 |
26 | /* Grid */
27 |
28 | #grid {
29 |
30 | .cols(@cols,@width,@gutter){
31 | .border-box();
32 | width: ((@cols * @width) + ((@cols - 1) * @gutter));
33 | margin-left: @gutter;
34 | position: relative;
35 | display: inline;
36 | float: left;
37 | min-height: 1px;
38 | &:first-child {
39 | margin-left: 0;
40 | }
41 | &:last-child {
42 | float: right;
43 | }
44 | }
45 |
46 | }
47 |
48 | .grid-fixed,.grid-fluid {
49 | .clearfix;
50 | .row {
51 | .border-box();
52 | display: block;
53 | width: 100%;
54 | margin: 0 auto;
55 | .clearfix;
56 |
57 | .center,.center:last-child {
58 | float: none;
59 | display: block;
60 | margin: 0 auto;
61 | }
62 | }
63 | }
64 |
65 | .grid-fixed {
66 | @total-width: (@fixed-column-width*@fixed-columns) + (@fixed-gutter-width*(@fixed-columns - 1));
67 | @column-width: @fixed-column-width;
68 | @gutter-width: @fixed-gutter-width;
69 | @columns: @fixed-columns;
70 | width: @total-width;
71 |
72 | /* This is duplicated in both classes. Unavoidable. */
73 | .colX (@index) when (@index > 0) {
74 | (~".col@{index}") {
75 | #grid > .cols(@index,@column-width,@gutter-width);
76 | }
77 | .colX(@index - 1);
78 | }
79 | .colX (0) {}
80 | .colX(@columns);
81 |
82 | .offsetX (@index) when (@index > 0) {
83 | (~".offset@{index}") {
84 | margin-left: (@index * @column-width) + ((@index + 1) * @gutter-width);
85 | }
86 | .offsetX(@index - 1);
87 | }
88 | .offsetX (0) {}
89 | .offsetX(@columns - 1);
90 |
91 | .pushX (@index) when (@index > 0) {
92 | (~".push@{index}") {
93 | left: @index * (@column-width + @gutter-width);
94 | }
95 | .pushX(@index - 1);
96 | }
97 | .pushX (0) {}
98 | .pushX(@columns - 1);
99 |
100 | .pullX (@index) when (@index > 0) {
101 | (~".pull@{index}") {
102 | right: @index * (@column-width + @gutter-width);
103 | }
104 | .pullX(@index - 1);
105 | }
106 | .pullX (0) {}
107 | .pullX(@columns - 1);
108 | }
109 |
110 |
111 | .grid-fluid {
112 | @total-width: 100%;
113 | @column-width: @fluid-column-width;
114 | @gutter-width: @fluid-gutter-width;
115 | @columns: @fluid-columns;
116 | width: @total-width;
117 |
118 | /* This is duplicated in both classes. Unavoidable. */
119 | .colX (@index) when (@index > 0) {
120 | (~".col@{index}") {
121 | #grid > .cols(@index,@column-width,@gutter-width);
122 | }
123 | .colX(@index - 1);
124 | }
125 | .colX (0) {}
126 | .colX(@columns);
127 |
128 | .offsetX (@index) when (@index > 0) {
129 | (~".offset@{index}") {
130 | margin-left: (@index * @column-width) + ((@index + 1) * @gutter-width);
131 | }
132 | .offsetX(@index - 1);
133 | }
134 | .offsetX (0) {}
135 | .offsetX(@columns - 1);
136 |
137 | .pushX (@index) when (@index > 0) {
138 | (~".push@{index}") {
139 | left: @index * (@column-width + @gutter-width);
140 | }
141 | .pushX(@index - 1);
142 | }
143 | .pushX (0) {}
144 | .pushX(@columns - 1);
145 |
146 | .pullX (@index) when (@index > 0) {
147 | (~".pull@{index}") {
148 | right: @index * (@column-width + @gutter-width);
149 | }
150 | .pullX(@index - 1);
151 | }
152 | .pullX (0) {}
153 | .pullX(@columns - 1);
154 | }
155 |
156 |
157 | @media all and (max-width: @mobile-break-width) {
158 |
159 | // Reset all columns to full width
160 | .grid-fixed {
161 | .colX (@index) when (@index > 0) {
162 | (~".col@{index}") {
163 | width: 100%;
164 | margin: 0;
165 | left: 0;
166 | right: 0;
167 | }
168 | .colX(@index - 1);
169 | }
170 | .colX (0) {}
171 | .colX(@fixed-columns);
172 | }
173 | .grid-fluid {
174 | .colX (@index) when (@index > 0) {
175 | (~".col@{index}") {
176 | width: 100%;
177 | margin: 0;
178 | left: 0;
179 | right: 0;
180 | }
181 | .colX(@index - 1);
182 | }
183 | .colX (0) {}
184 | .colX(@fluid-columns);
185 | }
186 |
187 | .grid-fixed, .grid-fluid {
188 | @total-width: 100%;
189 | @column-width: @mobile-column-width;
190 | @gutter-width: @mobile-gutter-width;
191 | @columns: @mobile-columns;
192 | width: @total-width;
193 |
194 | .m-colX (@index) when (@index > 0) {
195 | (~".m-col@{index}") {
196 | #grid > .cols(@index,@column-width,@gutter-width);
197 | }
198 | .m-colX(@index - 1);
199 | }
200 | .m-colX (0) {}
201 | .m-colX(@mobile-columns);
202 |
203 | .m-offsetX (@index) when (@index > 0) {
204 | (~".m-offset@{index}") {
205 | margin-left: (@index * @column-width) + ((@index + 1) * @gutter-width);
206 | }
207 | .m-offsetX(@index - 1);
208 | }
209 | .m-offsetX (0) {}
210 | .m-offsetX(@columns - 1);
211 |
212 | .m-pushX (@index) when (@index > 0) {
213 | (~".m-push@{index}") {
214 | left: @index * (@column-width + @gutter-width);
215 | }
216 | .m-pushX(@index - 1);
217 | }
218 | .m-pushX (0) {}
219 | .m-pushX(@columns - 1);
220 |
221 | .m-pullX (@index) when (@index > 0) {
222 | (~".m-pull@{index}") {
223 | right: @index * (@column-width + @gutter-width);
224 | }
225 | .m-pullX(@index - 1);
226 | }
227 | .m-pullX (0) {}
228 | .m-pullX(@columns - 1);
229 | }
230 |
231 | }
232 |
--------------------------------------------------------------------------------
/styles/base/idioms.less:
--------------------------------------------------------------------------------
1 | /**
2 | * New Media Campaigns Idioms
3 | *
4 | * These are common patterns we use in all of our
5 | * projects. They are consolidated here to keep code DRY.
6 | *
7 | * Listing
8 | * * .no-text, .text-replace
9 | * * .no-list
10 | * * .no-form
11 | * * .fixed-width(@width)
12 | * * .column-width(@width)
13 | * * .column-left(@width)
14 | * * .column-right(@width)
15 | * * .full-size
16 | * * .absolute-default
17 | * * .absolute-fullsize
18 | * * .clearfix
19 | */
20 |
21 | /* Hides text when using image replacement */
22 | .no-text, .text-replace{
23 | overflow: hidden;
24 | text-indent: 100%;
25 | white-space: nowrap;
26 | }
27 |
28 | /* Removes bullets, margin, and padding from list */
29 | .no-list{
30 | list-style: none;
31 | margin: 0;
32 | padding: 0;
33 | }
34 |
35 | /* Removes webkit styling from form element */
36 | .no-form{
37 | border: none;
38 | margin: 0;
39 | padding: 0;
40 | -webkit-appearance: none;
41 | }
42 |
43 | /* Center a fixed width container */
44 | .fixed-width(@width) {
45 | margin: 0 auto;
46 | width: @width;
47 | }
48 |
49 | /* Adds left or right columns (e.g. content and sidebar) */
50 | .column-width(@width){
51 | display: inline;
52 | width: @width;
53 | }
54 | .column-left(@width){
55 | .column-width(@width);
56 | float: left;
57 | }
58 | .column-right(@width){
59 | .column-width(@width);
60 | float: right;
61 | }
62 |
63 | /* Set width and height of element to that of its parent */
64 | .full-size{
65 | height: 100%;
66 | width: 100%;
67 | }
68 |
69 | /* Position element absolutely to 0,0 */
70 | .absolute-default{
71 | position: absolute;
72 | left: 0;
73 | top: 0;
74 | }
75 |
76 | /* Position element absolutely and set its width and height to that of its parent (useful for slideshows) */
77 | .absolute-fullsize{
78 | .absolute-default;
79 | .full-size;
80 | }
81 |
82 | /* The micro clearfix http://nicolasgallagher.com/micro-clearfix-hack/ */
83 | .clearfix {
84 | *zoom:1;
85 |
86 | &:before,
87 | &:after {
88 | content:"";
89 | display:table;
90 | }
91 | &:after {
92 | clear:both;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/styles/base/reset.less:
--------------------------------------------------------------------------------
1 | /**
2 | * html5doctor.com Reset Stylesheet
3 | * v1.6.1
4 | * Last Updated: 2010-09-17
5 | * Author: Richard Clark - http://richclarkdesign.com
6 | * Twitter: @rich_clark
7 | */
8 |
9 | html, body, div, span, object, iframe,
10 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
11 | abbr, address, cite, code,
12 | del, dfn, em, img, ins, kbd, q, samp,
13 | small, strong, sub, sup, var,
14 | b, i,
15 | dl, dt, dd, ol, ul, li,
16 | fieldset, form, label, legend,
17 | table, caption, tbody, tfoot, thead, tr, th, td,
18 | article, aside, canvas, details, figcaption, figure,
19 | footer, header, hgroup, menu, nav, section, summary,
20 | time, mark, audio, video {
21 | margin:0;
22 | padding:0;
23 | border:0;
24 | outline:0;
25 | font-size:100%;
26 | vertical-align:baseline;
27 | background:transparent;
28 | }
29 |
30 | body {
31 | line-height:1;
32 | }
33 |
34 | article,aside,details,figcaption,figure,
35 | footer,header,hgroup,menu,nav,section {
36 | display:block;
37 | }
38 |
39 | nav ul {
40 | list-style:none;
41 | }
42 |
43 | blockquote, q {
44 | quotes:none;
45 | }
46 |
47 | blockquote:before, blockquote:after,
48 | q:before, q:after {
49 | content:'';
50 | content:none;
51 | }
52 |
53 | a {
54 | margin:0;
55 | padding:0;
56 | font-size:100%;
57 | vertical-align:baseline;
58 | background:transparent;
59 | }
60 |
61 | /* change colours to suit your needs */
62 | ins {
63 | background-color:#ff9;
64 | color:#000;
65 | text-decoration:none;
66 | }
67 |
68 | /* change colours to suit your needs */
69 | mark {
70 | background-color:#ff9;
71 | color:#000;
72 | font-style:italic;
73 | font-weight:bold;
74 | }
75 |
76 | del {
77 | text-decoration: line-through;
78 | }
79 |
80 | abbr[title], dfn[title] {
81 | border-bottom:1px dotted;
82 | cursor:help;
83 | }
84 |
85 | table {
86 | border-collapse:collapse;
87 | border-spacing:0;
88 | }
89 |
90 | /* change border colour to suit your needs */
91 | hr {
92 | display:block;
93 | height:1px;
94 | border:0;
95 | border-top:1px solid #cccccc;
96 | margin:1em 0;
97 | padding:0;
98 | }
99 |
100 | input, select {
101 | vertical-align:middle;
102 | }
--------------------------------------------------------------------------------
/styles/base/spacing.less:
--------------------------------------------------------------------------------
1 | /**
2 | * Spacing
3 | *
4 | * This LESS file defines margins and paddings for block-level
5 | * elements. Helper classes are included for use elsewhere
6 | * in site styles.
7 | */
8 |
9 | /* Settings */
10 |
11 | @import 'variables.less';
12 |
13 | /*
14 | @baseline: @baseline;
15 | */
16 |
17 | /**
18 | * Spacing
19 | * p, m, lh = padding, margin, line-height
20 | * a, t, r, b, l, h, v = all, top, right, bottom, left, horizontal, vertical
21 | * n, h, s, d = none(0px), half(@baseline / 2), single(@baseline), double(@baseline * 2), none(0px)
22 | */
23 |
24 | .ptn, .pvn, .pan{
25 | padding-top: 0 !important
26 | }
27 | .pth, .pvh, .pah{
28 | padding-top: @baseline / 2 !important
29 | }
30 | .pts, .pvs, .pas{
31 | padding-top: @baseline !important
32 | }
33 | .ptd, .pvd, .pad{
34 | padding-top: @baseline * 2 !important
35 | }
36 | .prn, .phn, .pan{
37 | padding-right: 0 !important
38 | }
39 | .prh, .phh, .pah{
40 | padding-right: @baseline / 2 !important
41 | }
42 | .prs, .phs, .pas{
43 | padding-right: @baseline !important
44 | }
45 | .prd, .phd, .pad{
46 | padding-right: @baseline * 2 !important
47 | }
48 | .pbn, .pvn, .pan{
49 | padding-bottom: 0 !important
50 | }
51 | .pbh, .pvh, .pah{
52 | padding-bottom: @baseline / 2 !important
53 | }
54 | .pbs, .pvs, .pas{
55 | padding-bottom: @baseline !important
56 | }
57 | .pbd, .pvd, .pad{
58 | padding-bottom: @baseline * 2 !important
59 | }
60 | .pln, .phn, .pan{
61 | padding-left: 0 !important
62 | }
63 | .plh, .phh, .pah{
64 | padding-left: @baseline / 2 !important
65 | }
66 | .pls, .phs, .pas{
67 | padding-left: @baseline !important
68 | }
69 | .pld, .phd, .pad{
70 | padding-left: @baseline * 2 !important
71 | }
72 | .mtn, .mvn, .man{
73 | margin-top: 0 !important
74 | }
75 | .mth, .mvh, .mah{
76 | margin-top: @baseline / 2 !important
77 | }
78 | .mts, .mvs, .mas{
79 | margin-top: @baseline !important
80 | }
81 | .mtd, .mvd, .mad{
82 | margin-top: @baseline * 2 !important
83 | }
84 | .mrn, .mhn, .man{
85 | margin-right: 0 !important
86 | }
87 | .mrh, .mhh, .mah{
88 | margin-right: @baseline / 2 !important
89 | }
90 | .mrs, .mhs, .mas{
91 | margin-right: @baseline !important
92 | }
93 | .mrd, .mhd, .mad{
94 | margin-right: @baseline * 2 !important
95 | }
96 | .mbn, .mvn, .man{
97 | margin-bottom: 0 !important
98 | }
99 | .mbh, .mvh, .mah{
100 | margin-bottom: @baseline / 2 !important
101 | }
102 | .mbs, .mvs, .mas{
103 | margin-bottom: @baseline !important
104 | }
105 | .mbd, .mvd, .mad{
106 | margin-bottom: @baseline * 2 !important
107 | }
108 | .mln, .mhn, .man{
109 | margin-left: 0 !important
110 | }
111 | .mlh, .mhh, .mah{
112 | margin-left: @baseline / 2 !important
113 | }
114 | .mls, .mhs, .mas{
115 | margin-left: @baseline !important
116 | }
117 | .mld, .mhd, .mad{
118 | margin-left: @baseline * 2 !important
119 | }
120 | .lhh {
121 | line-height: @baseline / 2 !important;
122 | }
123 | .lhs {
124 | line-height: @baseline !important;
125 | }
126 | .lhd {
127 | line-height: @baseline * 2 !important;
128 | }
129 | .lhn {
130 | line-height: 0 !important;
131 | }
132 |
--------------------------------------------------------------------------------
/styles/base/typography.less:
--------------------------------------------------------------------------------
1 | /**
2 | * Name Here
3 | *
4 | * @version 1.0
5 | * @package Name Here
6 | * @author New Media Campaigns
7 | * @copyright 2012 New Media Campaigns
8 | * @link http://www.newmediacampaigns.com
9 | *
10 | * Copyright (c) 2012 New Media Campaigns
11 | *
12 | * Permission is hereby granted, free of charge, to any person obtaining a copy
13 | * of this software and associated documentation files (the "Software"), to deal
14 | * in the Software without restriction, including without limitation the rights
15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | * copies of the Software, and to permit persons to whom the Software is furnished
17 | * to do so, subject to the following conditions:
18 | *
19 | * The above copyright notice and this permission notice shall be included in all
20 | * copies or substantial portions of the Software.
21 | *
22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 | * THE SOFTWARE.
29 | */
30 |
31 | /**
32 | * Typography
33 | *
34 | * This LESS file defines the baseline, color, font-size, and other typographical
35 | * styles for text elements.
36 | */
37 |
38 | /* Settings */
39 |
40 | @import 'variables.less';
41 |
42 | /*
43 | @baseline: @baseline;
44 |
45 | @body-color: @body-color;
46 | @body-font-family: @body-font-family;
47 | @body-font-size: @body-font-size;
48 | @body-accent-color: @body-accent-color;
49 |
50 | @header-color: @header-clor;
51 | @header-font-family: @header-font-family;
52 | @header-font-weight: @header-font-weight;
53 | */
54 |
55 | /* Base */
56 |
57 | html, body {
58 | font-family: @body-font-family;
59 | color: @body-color;
60 | font-size: @body-font-size;
61 | .lhs;
62 | }
63 |
64 | /* Block-level */
65 |
66 | h1, h2, h3, h4, h5, h6, ul, ol, dl, p, blockquote, table, form, pre{
67 | .mbs;
68 | }
69 |
70 | /* Headers */
71 |
72 | .all-headers{
73 | color: @header-color;
74 | font-family: @header-font-family;
75 | font-weight: @header-font-weight;
76 | }
77 | h1, .alpha{
78 | .all-headers;
79 | font-size: @body-font-size * 2.5;
80 | .lhd;
81 | }
82 | h2, .beta{
83 | .all-headers;
84 | font-size: @body-font-size * 1.75;
85 | .lhd;
86 | }
87 | h3, .gamma{
88 | .all-headers;
89 | font-size: @body-font-size * 1.2;
90 | .lhd;
91 | .mbn;
92 | }
93 | h4, .delta{
94 | .all-headers;
95 | font-size: @body-font-size * 1;
96 | .lhd;
97 | .mbn;
98 | }
99 | h5, .epsilon{
100 | .all-headers;
101 | font-size: @body-font-size * 1;
102 | .lhs;
103 | .mbn
104 | }
105 | h6, .zeta{
106 | .all-headers;
107 | font-size: @body-font-size * 1;
108 | .lhs;
109 | .mbn;
110 | }
111 |
112 | /* Headers (above scale) */
113 |
114 | .giga{
115 | .all-headers;
116 | font-size: @body-font-size * 6;
117 | line-height: @baseline * 4;
118 | }
119 | .mega{
120 | .all-headers;
121 | font-size: @body-font-size * 5;
122 | line-height: @baseline * 3;
123 | }
124 | .kilo{
125 | .all-headers;
126 | font-size: @body-font-size * 4;
127 | line-height: @baseline * 3;
128 | }
129 |
130 | /* Headers (below scale) */
131 |
132 | .milli{
133 | .all-headers;
134 | font-size: @body-font-size * 0.8;
135 | }
136 |
137 | /* Text */
138 |
139 | a{
140 | color: @body-accent-color;
141 | }
142 | a:link{
143 | text-decoration: underline;
144 | }
145 | a:visited{
146 |
147 | }
148 | a:hover{
149 | text-decoration: none;
150 | }
151 | a:active{
152 |
153 | }
154 | a:focus{
155 |
156 | }
157 | small {
158 | font-size: 80%;
159 | }
160 | sup, sub {
161 | font-size: 80%;
162 | .lhn;
163 | }
164 | sup {
165 | vertical-align: super;
166 | }
167 | sub {
168 | vertical-align: sub;
169 | }
170 |
171 | .lead{
172 | color: darken(@body-color, 20%);
173 | font-size: @baseline * 0.8;
174 | }
175 |
176 | blockquote{
177 | border-left: 5px solid fade(#000, 10%);
178 | .mhs;
179 | .pls;
180 | color: lighten(@body-color, 20%);
181 | font-size: @baseline * 0.8;
182 | font-style: italic;
183 |
184 | :last-child{
185 | .mbn;
186 | }
187 | small{
188 | color: fade(#000, 50%);
189 | font-size: @baseline * 0.7;
190 | font-style: normal;
191 | }
192 | }
193 | pre, code, kbd{
194 | background: #F8F8F8;
195 | border: 1px solid #EAEAEA;
196 | .border-radius(3px);
197 | margin: 0 2px;
198 | padding: 0 5px;
199 | font-family: Consolas, "Courier New", Courier, mono;
200 | font-size: @body-font-size * 0.9;
201 | color: @body-color;
202 | word-wrap: break-word;
203 | }
204 | pre{
205 | .mhs;
206 | .pah;
207 |
208 | code{
209 | border: none;
210 | .man;
211 | .pan;
212 | }
213 | }
214 | a code{
215 | background: none;
216 | border: none;
217 | .pan;
218 | .man;
219 | }
220 | strong{
221 | font-weight: bold;
222 | }
223 | em{
224 | font-style: italic;
225 | }
226 | ol, ul, dl{
227 | .mls;
228 | .pls;
229 | ol,ul {
230 | .mbn;
231 | }
232 | }
233 | dt{
234 | font-weight: bold;
235 | }
236 | dd{
237 | .mls;
238 | }
239 |
240 | .table{
241 | border-collapse: collapse;
242 | border-spacing: 0;
243 | width: 100%;
244 | }
245 | .table th, .table td{
246 | border-top: 1px solid fade(#000, 10%);
247 | padding: 8px;
248 | text-align: left;
249 | }
250 | .table thead th{
251 | vertical-align: bottom;
252 | font-weight: bold;
253 | }
254 | .table thead tr:first-child th{
255 | border-top: none;
256 | }
257 | .table-bordered{
258 | border: 1px solid fade(#000, 10%);
259 | border-collapse: separate;
260 | border-left: none;
261 | .border-radius(4px);
262 |
263 | th, td{
264 | border-left: 1px solid fade(#000, 10%);
265 | }
266 | thead:last-child tr:last-child th:first-child,
267 | tbody:last-child tr:last-child td:first-child{
268 | .border-radius(0 0 0 4px);
269 | }
270 | }
271 | .table-striped{
272 | tbody tr:nth-child(odd) td{
273 | background: fade(@body-accent-color, 4%);
274 | }
275 | }
276 |
277 | /* ==========================================================================
278 | Alerts
279 | ========================================================================== */
280 |
281 | .alert{
282 | background: #FCF8E3;
283 | border: 1px solid #FBEED5;
284 | .border-radius(4px);
285 | .mbs;
286 | .pah;
287 | color: #C09853;
288 | }
289 | .alert-success{
290 | .alert;
291 | background-color: #DFF0D8;
292 | border-color: #D6E9C6;
293 | color: #468847;
294 | }
295 | .alert-error{
296 | .alert;
297 | background-color: #F2DEDE;
298 | border-color: #EED3D7;
299 | color: #B94A48;
300 | }
301 | .alert-info{
302 | .alert;
303 | background-color: #D9EDF7;
304 | border-color: #BCE8F1;
305 | color: #3A87AD;
306 | }
307 |
308 | /* ==========================================================================
309 | Forms
310 | ========================================================================== */
311 |
312 | label{
313 | display: block;
314 | font-weight: bold;
315 |
316 | .req{
317 | color: @body-accent-color;
318 | font-weight: bold;
319 | }
320 | }
321 | input.text,
322 | textarea,
323 | select,
324 | .radio-group,
325 | .checkbox-group{
326 | .mbs;
327 | }
328 | input.text, textarea{
329 | .no-form;
330 | background: #EEE;
331 | border: 1px solid #CCC;
332 | .border-box;
333 | .border-radius(4px);
334 | .inner-shadow(fade(#000, 10%) 0 1px 3px);
335 | height: @baseline * 1.5;
336 | .pah;
337 | width: 100%;
338 | }
339 | textarea{
340 | height: @baseline * 6;
341 | }
342 | select{
343 | min-width: 30%;
344 | }
345 | .checkbox-group label,
346 | .radio-group label{
347 | font-weight: normal;
348 | }
349 | .error{
350 | background-color: #F2DEDE !important;
351 | border-color: red !important;
352 | outline-color: red !important;
353 | color: red !important;
354 | }
355 |
--------------------------------------------------------------------------------
/styles/base/variables.less:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | Spacing
3 | ========================================================================== */
4 |
5 | @baseline: 20px;
6 |
7 | /* ==========================================================================
8 | Typography
9 | ========================================================================== */
10 |
11 | @body-color: #555;
12 | @body-font-family: "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, "Lucida Grande", sans-serif;
13 | @body-font-size: 14px;
14 | @body-accent-color: #f00;
15 |
16 | @header-color: #000;
17 | @header-font-family: "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, "Lucida Grande", sans-serif;
18 | @header-font-weight: bold;
19 |
20 | /* ==========================================================================
21 | Grid
22 | ========================================================================== */
23 |
24 | @fixed-column-width: 40px;
25 | @fixed-gutter-width: 20px;
26 | @fixed-columns: 12;
27 |
28 | @fluid-column-width: 4.3%;
29 | @fluid-gutter-width: 4.4%;
30 | @fluid-columns: 12;
31 |
32 | @mobile-break-width: 480px;
33 | @mobile-column-width: 20%;
34 | @mobile-gutter-width: 6.6666%;
35 | @mobile-columns: 4;
36 |
37 | /* ==========================================================================
38 | Buttons
39 | ========================================================================== */
40 |
41 | @button-color: #DDD;
42 | @button-primary-color: #0055CC;
43 | @button-info-color: #2F96B4;
44 | @button-success-color: #51A351;
45 | @button-warning-color: #FAA732;
46 | @button-danger-color: #BD362F;
47 |
48 | /* ==========================================================================
49 | Site Variables
50 | ========================================================================== */
51 |
52 | @import "../site/variables";
--------------------------------------------------------------------------------
/styles/print.css:
--------------------------------------------------------------------------------
1 | body, .site-title, h1, h2, h3{
2 | font-family: 'Georgia' !important;
3 | }
4 |
5 |
6 | nav.site-navigation, a.fork-me, a.top{
7 | display:none;
8 | }
9 |
10 | div.site-content{
11 | padding: 20px 40px 40px 20px;
12 | }
--------------------------------------------------------------------------------
/styles/site/site-content.less:
--------------------------------------------------------------------------------
1 | .site-content{
2 | padding: 20px 40px 40px 320px;
3 |
4 | h1{
5 | clear: both;
6 | }
7 | }
8 | .interior-site-content{
9 | .pad;
10 | }
11 | .top{
12 | background: #333;
13 | display: inline-block;
14 | float: right;
15 | margin-right: -40px;
16 | padding: 4px 8px;
17 | color: #FFF;
18 | font-size: 12px;
19 | text-decoration: none !important;
20 | }
21 |
--------------------------------------------------------------------------------
/styles/site/site-footer.less:
--------------------------------------------------------------------------------
1 | .site-footer{
2 | clear: both;
3 | .ptd;
4 | font-size: 13px;
5 | text-align: center;
6 | }
7 | .site-footer img{
8 | margin-left: 2px;
9 | position: relative;
10 | top: -2px;
11 | vertical-align: middle;
12 | }
13 | .site-footer ul{
14 | list-style: none;
15 | .mhn;
16 | .phn;
17 | }
18 |
--------------------------------------------------------------------------------
/styles/site/site-header.less:
--------------------------------------------------------------------------------
1 | .site-header{
2 | position: relative;
3 | z-index: 1;
4 | text-align: center;
5 | }
6 | .site-title{
7 | .mbn;
8 | font-family: 'Alfa Slab One';
9 | font-size: @baseline * 4;
10 | font-weight: normal !important;
11 | line-height: @baseline * 5 !important;
12 |
13 | a{
14 | text-decoration: none;
15 | }
16 | }
17 | .site-slogan{
18 | font-weight: normal;
19 | }
20 | .fork-me, .fork-me img{
21 | position: absolute;
22 | top: 0;
23 | right: 0;
24 | }
25 | .fork-me{
26 | z-index: 2;
27 | }
28 |
29 | .interior-site-header{
30 | background: #EEE;
31 | .inner-shadow(fade(#000, 7%) 0 0 40px);
32 | .pas;
33 | text-align: center;
34 |
35 | .site-title{
36 | font-size: @baseline * 2;
37 | line-height: @baseline * 2 !important;
38 | }
39 | .site-slogan{
40 | .mbs;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/styles/site/site-navigation.less:
--------------------------------------------------------------------------------
1 | .site-navigation{
2 | background: #EEE;
3 | .inner-shadow(fade(#000, 7%) 0 0 40px);
4 | .pas;
5 | position: fixed;
6 | top: 0;
7 | bottom: 0;
8 | overflow: auto;
9 | width: 240px;
10 | }
11 | .build-date{
12 | .mbs;
13 | color: #AAA;
14 | font-family: Helvetica, Arial, sans-serif;
15 | font-size: 11px;
16 | }
17 | .site-navigation ul{
18 | .man;
19 | .pan;
20 | .no-list;
21 | font-size: 16px;
22 | margin-bottom: 10px;
23 | }
24 | .site-navigation > ul > li{
25 | margin-bottom: 10px;
26 | }
27 | .site-navigation a{
28 | text-decoration: underline;
29 |
30 | &:hover{
31 | text-decoration: none;
32 | }
33 | }
34 | .site-navigation a.active{
35 | background-color: #ff9;
36 | }
37 | .site-navigation ul ul{
38 | .mls;
39 | .pth;
40 | font-size: 12px;
41 |
42 | a{
43 | text-decoration: none;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/styles/site/variables.less:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | Spacing
3 | ========================================================================== */
4 |
5 | @baseline: 20px;
6 |
7 | /* ==========================================================================
8 | Typography
9 | ========================================================================== */
10 |
11 | @body-color: #666;
12 | @body-font-family: "Droid Serif", Georgia, "Times New Roman", Times, serif;
13 | @body-font-size: 14px;
14 | @body-accent-color: #000;
15 |
16 | @header-color: #111;
17 | @header-font-family: "Droid Serif", Georgia, "Times New Roman", Times, serif;
18 | @header-font-weight: 700;
19 |
20 | /* ==========================================================================
21 | Grid
22 | ========================================================================== */
23 |
24 | @fixed-column-width: 60px;
25 | @fixed-gutter-width: 20px;
26 | @fixed-columns: 12;
27 |
28 | @fluid-column-width: 4.3%;
29 | @fluid-gutter-width: 4.4%;
30 | @fluid-columns: 12;
31 |
32 | @mobile-break-width: 480px;
33 | @mobile-column-width: 20%;
34 | @mobile-gutter-width: 6.6666%;
35 | @mobile-columns: 4;
36 |
37 | /* ==========================================================================
38 | Buttons
39 | ========================================================================== */
40 |
41 | @button-color: #DDD;
42 | @button-primary-color: #0055CC;
43 | @button-info-color: #2F96B4;
44 | @button-success-color: #51A351;
45 | @button-warning-color: #FAA732;
46 | @button-danger-color: #BD362F;
47 |
--------------------------------------------------------------------------------
/styles/syntax.css:
--------------------------------------------------------------------------------
1 | .highlight { background: #ffffff; margin: 0 4px; font-size: 0.8em; }
2 | .highlight .c { color: #999988; font-style: italic } /* Comment */
3 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
4 | .highlight .k { font-weight: bold } /* Keyword */
5 | .highlight .o { font-weight: bold } /* Operator */
6 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
7 | .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
8 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
9 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
10 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
11 | .highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
12 | .highlight .ge { font-style: italic } /* Generic.Emph */
13 | .highlight .gr { color: #aa0000 } /* Generic.Error */
14 | .highlight .gh { color: #999999 } /* Generic.Heading */
15 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
16 | .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
17 | .highlight .go { color: #888888 } /* Generic.Output */
18 | .highlight .gp { color: #555555 } /* Generic.Prompt */
19 | .highlight .gs { font-weight: bold } /* Generic.Strong */
20 | .highlight .gu { color: #aaaaaa } /* Generic.Subheading */
21 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */
22 | .highlight .kc { font-weight: bold } /* Keyword.Constant */
23 | .highlight .kd { font-weight: bold } /* Keyword.Declaration */
24 | .highlight .kp { font-weight: bold } /* Keyword.Pseudo */
25 | .highlight .kr { font-weight: bold } /* Keyword.Reserved */
26 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
27 | .highlight .m { color: #009999 } /* Literal.Number */
28 | .highlight .s { color: #d14 } /* Literal.String */
29 | .highlight .na { color: #008080 } /* Name.Attribute */
30 | .highlight .nb { color: #0086B3 } /* Name.Builtin */
31 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
32 | .highlight .no { color: #008080 } /* Name.Constant */
33 | .highlight .ni { color: #800080 } /* Name.Entity */
34 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
35 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
36 | .highlight .nn { color: #555555 } /* Name.Namespace */
37 | .highlight .nt { color: #000080 } /* Name.Tag */
38 | .highlight .nv { color: #008080 } /* Name.Variable */
39 | .highlight .ow { font-weight: bold } /* Operator.Word */
40 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */
41 | .highlight .mf { color: #009999 } /* Literal.Number.Float */
42 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */
43 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */
44 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */
45 | .highlight .sb { color: #d14 } /* Literal.String.Backtick */
46 | .highlight .sc { color: #d14 } /* Literal.String.Char */
47 | .highlight .sd { color: #d14 } /* Literal.String.Doc */
48 | .highlight .s2 { color: #d14 } /* Literal.String.Double */
49 | .highlight .se { color: #d14 } /* Literal.String.Escape */
50 | .highlight .sh { color: #d14 } /* Literal.String.Heredoc */
51 | .highlight .si { color: #d14 } /* Literal.String.Interpol */
52 | .highlight .sx { color: #d14 } /* Literal.String.Other */
53 | .highlight .sr { color: #009926 } /* Literal.String.Regex */
54 | .highlight .s1 { color: #d14 } /* Literal.String.Single */
55 | .highlight .ss { color: #990073 } /* Literal.String.Symbol */
56 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
57 | .highlight .vc { color: #008080 } /* Name.Variable.Class */
58 | .highlight .vg { color: #008080 } /* Name.Variable.Global */
59 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */
60 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
--------------------------------------------------------------------------------