<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Gasol Wu]]></title><description><![CDATA[Just a blogging platform.]]></description><link>https://blog.gasol.tw/</link><image><url>https://blog.gasol.tw/favicon.png</url><title>Gasol Wu</title><link>https://blog.gasol.tw/</link></image><generator>Ghost 3.42</generator><lastBuildDate>Thu, 26 Mar 2026 22:48:32 GMT</lastBuildDate><atom:link href="https://blog.gasol.tw/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[W3Techs 發佈 2020 年度網站技術調查報告]]></title><description><![CDATA[<p>在閱讀<a href="https://w3techs.com/blog/entry/web_technologies_of_the_year_2020">報告</a>之前，先了解其<a href="https://w3techs.com/disclaimer">免責聲明</a>和<a href="https://w3techs.com/technologies">調查方法</a>是比較好的習慣，我自己覺得都還算合理。</p><p>這份結果以 2020 年 1 月 1 日至 2021 年 1 月 1 日以網站為單位的數量差來比較排名，不是用常見的市場佔比，這樣才有機會讓比較火的新人冒出頭來。也因此像是 LiteSpeed、Bitrix 和 Scala 才能浮上台面。</p><p>看的出來 2020 年疫情稍微影響了網路技術的風貌，電商架站平台如 Shopify 和 Wix 等高速成長，對網站技術的貢獻就如同股價一樣成長是可以想像的事。</p><figure class="kg-card kg-image-card"><img src="https://blog.gasol.tw/content/images/2021/01/Screen-Shot-2021-01-07-at-12.09.21-PM-1.png" class="kg-image"></figure><p>作業系統的類別說明說 Unix 系統包含 Unix-like 系統如 Linux，那第二、第三名的 Ubuntu 和 OpenBSD</p>]]></description><link>https://blog.gasol.tw/w3techs-2020/</link><guid isPermaLink="false">5ff693a3044929007c350131</guid><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Wed, 06 Jan 2021 20:53:40 GMT</pubDate><media:content url="https://blog.gasol.tw/content/images/2021/01/nasa-Q1p7bh3SHj8-unsplash--2-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.gasol.tw/content/images/2021/01/nasa-Q1p7bh3SHj8-unsplash--2-.jpg" alt="W3Techs 發佈 2020 年度網站技術調查報告"><p>在閱讀<a href="https://w3techs.com/blog/entry/web_technologies_of_the_year_2020">報告</a>之前，先了解其<a href="https://w3techs.com/disclaimer">免責聲明</a>和<a href="https://w3techs.com/technologies">調查方法</a>是比較好的習慣，我自己覺得都還算合理。</p><p>這份結果以 2020 年 1 月 1 日至 2021 年 1 月 1 日以網站為單位的數量差來比較排名，不是用常見的市場佔比，這樣才有機會讓比較火的新人冒出頭來。也因此像是 LiteSpeed、Bitrix 和 Scala 才能浮上台面。</p><p>看的出來 2020 年疫情稍微影響了網路技術的風貌，電商架站平台如 Shopify 和 Wix 等高速成長，對網站技術的貢獻就如同股價一樣成長是可以想像的事。</p><figure class="kg-card kg-image-card"><img src="https://blog.gasol.tw/content/images/2021/01/Screen-Shot-2021-01-07-at-12.09.21-PM-1.png" class="kg-image" alt="W3Techs 發佈 2020 年度網站技術調查報告"></figure><p>作業系統的類別說明說 Unix 系統包含 Unix-like 系統如 Linux，那第二、第三名的 Ubuntu 和 OpenBSD 又是什麼？難到 Ubuntu 不是 Linux，BSD 系統不像 Unix？在 <a href="https://w3techs.com/technologies/details/os-unix">Unix 的子分類</a>裡 Linux 佔比 38.4%，而 <a href="https://w3techs.com/technologies/details/os-linux">Linux 的子分類</a>裡 Ubuntu 佔比 48.3%。總之就是看不懂。</p>]]></content:encoded></item><item><title><![CDATA[ExDactyl 一週體驗工]]></title><description><![CDATA[<h2 id="-">前言</h2><p>遲來的心得文，就在六月已進入炎熱的夏天時的某日，突然收到了 ExDactyl 鍵盤的<a href="https://www.ergokb.tw/blogs/free_trial_exdactyl/#exdactyl-%E9%AB%94%E9%A9%97%E6%B4%BB%E5%8B%95">體驗邀請</a>，這才回想起好久之前曾經填表申請體驗過，現在能一償宿願，體驗這難能可貴的經驗，十足是一件開心的事。</p><h2 id="exdactyl">ExDactyl</h2><p><a href="https://github.com/LSChyi/ex-dactyl-keyboard">ExDactyl</a> 鍵盤是<a href="https://medium.com/@alan81920">李松錡</a>基於 <a href="https://github.com/adereth/dactyl-keyboard">Dactyl</a> 人體工學鍵盤重製改良的作品，因此它承襲了 Dactyl 的優點，包括曲面和分離式的設計、精巧的鍵位、機械式鍵軸、可程式化、開源放式原始碼，目前尚未量產，僅是創客精神的原型。</p><p>在認識它之前，身為一位使用鍵盤的老司機，我自己擁有一把 Keyboardio <a href="https://github.com/Gasol/Model01-Firmware">Model 01</a>，一把跟 ExDactyl 一樣有著奇特外型左右分離的鍵盤，因為 Model 01 的使用經驗，自然會拿來做比較的基準。</p><p>像這一類型的鍵盤其設計理念其實很相似，每一項設計的背後都有想要解決的問題，例如：分離式的設計可以讓你能任意擺放在舒適的位置，讓碗關節不必處在奇怪的角度打字；鍵位的安排是要縮小五隻長短不一的手指頭的移動距離，做工愈小愈省力；鍵帽的弧度是讓觸感提升的小細節；</p>]]></description><link>https://blog.gasol.tw/exdactyl-experience/</link><guid isPermaLink="false">5ef9f5ed46973b0075713122</guid><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Mon, 29 Jun 2020 06:28:26 GMT</pubDate><media:content url="https://blog.gasol.tw/content/images/2020/06/photo_2020-06-29-22.21.01.jpeg" medium="image"/><content:encoded><![CDATA[<h2 id="-">前言</h2><img src="https://blog.gasol.tw/content/images/2020/06/photo_2020-06-29-22.21.01.jpeg" alt="ExDactyl 一週體驗工"><p>遲來的心得文，就在六月已進入炎熱的夏天時的某日，突然收到了 ExDactyl 鍵盤的<a href="https://www.ergokb.tw/blogs/free_trial_exdactyl/#exdactyl-%E9%AB%94%E9%A9%97%E6%B4%BB%E5%8B%95">體驗邀請</a>，這才回想起好久之前曾經填表申請體驗過，現在能一償宿願，體驗這難能可貴的經驗，十足是一件開心的事。</p><h2 id="exdactyl">ExDactyl</h2><p><a href="https://github.com/LSChyi/ex-dactyl-keyboard">ExDactyl</a> 鍵盤是<a href="https://medium.com/@alan81920">李松錡</a>基於 <a href="https://github.com/adereth/dactyl-keyboard">Dactyl</a> 人體工學鍵盤重製改良的作品，因此它承襲了 Dactyl 的優點，包括曲面和分離式的設計、精巧的鍵位、機械式鍵軸、可程式化、開源放式原始碼，目前尚未量產，僅是創客精神的原型。</p><p>在認識它之前，身為一位使用鍵盤的老司機，我自己擁有一把 Keyboardio <a href="https://github.com/Gasol/Model01-Firmware">Model 01</a>，一把跟 ExDactyl 一樣有著奇特外型左右分離的鍵盤，因為 Model 01 的使用經驗，自然會拿來做比較的基準。</p><p>像這一類型的鍵盤其設計理念其實很相似，每一項設計的背後都有想要解決的問題，例如：分離式的設計可以讓你能任意擺放在舒適的位置，讓碗關節不必處在奇怪的角度打字；鍵位的安排是要縮小五隻長短不一的手指頭的移動距離，做工愈小愈省力；鍵帽的弧度是讓觸感提升的小細節；Palm key 就是個不必用小指頭按的 fn 鍵；Thumb key 就是將小指的壓力轉移到大姆指，特別是修飾鍵；自訂 layout 打造符合自己需求和習慣的鍵盤。</p><p>在收到 ExDactlyl 之前，我迫不期待想要體驗的就是它獨具特色的曲面設計，畢竟這是 Model 01 所沒有的。在這一週的體驗裡，我整理了三大體驗心得來分享。</p><h3 id="--1">一、曲面設計</h3><p>曲面的設計旨在減短指尖的移動距離，你可以利用自己的中指來回彎曲想像一下，指尖所劃出的線應該是呈一個弧線，而曲面的鍵恰恰貼合著這個弧線來縮短手指必須移動的距離。相反的，在一般的平面鍵盤上，如果目標鍵遠一點，例如 home row 之於數字鍵或 function 鍵，你就得伸長手指或者移動整個手部來觸碰到。</p><figure class="kg-card kg-image-card"><img src="https://blog.gasol.tw/content/images/2020/06/photo_2020-06-29-22.20.54.jpeg" class="kg-image" alt="ExDactyl 一週體驗工"></figure><p>一開始我並沒有感受到如預期的效果，總覺得哪裡怪怪的，一直到加上隨箱附上的手腕墊支撐著手，才有種豁然開朗的感覺。我認為關鍵在於手要懸浮在適當的位置才能獲得最佳體驗。這方面很難用文字來表達，總之每個人的手長的不一樣，搭配的椅子、桌子高度也不同，很難有一個 one size fit all 的最佳位置。</p><h3 id="--2">二、組合鍵障礙</h3><p>我收到的是一把 3D 印表機塑模出來的鍵盤 prototype，鍵帽表面並沒有刻上所代表的字元，就是所謂的「無刻」鍵帽。這我可以理解，做為一把可以隨時更改 layout 的鍵盤，刻上字元的鍵帽帶來的困擾我相信是大於它的價值。<br>而我一拿鍵盤時很興奮的就隨插即用，QWERTY-like 並沒有辦法阻止我的好奇心，用不著看過說明書，一般常用 <code>[a-zA-Z0-9]</code> 範圍的字元憑著肌肉記憶用摸就打的出來了，但隨之而來馬上就面臨到組合鍵的挑戰，因為 coding 常常需要打出像 <code>()</code>, <code>{}</code>, <code>[]</code> 成對的字元，identifier 變數命名也需要使用大寫英文，這些情況下需要使用 <code>SHIFT</code> 鍵，<code>ALT</code> 鍵則常常用來刪除前一個字 (word)，<code>CTRL</code> 鍵用來操作 emacs movement，諸如此類的情境都需要組合鍵。而在不熟悉組合鍵的情況下，寫 code 就卡卡的。最後才乖乖的上網翻了一下<a href="https://www.ergokb.tw/blogs/why_i_use_ergodox.html#%E8%B8%8F%E5%85%A5%E4%BA%BA%E9%AB%94%E5%B7%A5%E5%AD%B8%E9%8D%B5%E7%9B%A4%E4%B9%8B%E5%89%8D">答案</a>才漸漸上手，開始脫離打字障礙。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.ergokb.tw/why_I_use_ergodox/first_layer_key_def_thumbnail.png" class="kg-image" alt="ExDactyl 一週體驗工"><figcaption>main layer</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.ergokb.tw/why_I_use_ergodox/second_layer_key_def.png" class="kg-image" alt="ExDactyl 一週體驗工"><figcaption>second layer</figcaption></figure><p><br>從 layout 裡可以得知 ExDactyl 也有 layer 的概念，這個功能我在 Model 01 就認識了，非常之強大，在不同 layer 裡可以隨你自己喜歡放不同用途的 layout，你可以在不同軟體間切換不同 layout。<br>另外，在預設的 layout 偏好也可以觀察出作者的屬性，從蘋果鍵和 HJKL 鍵可以推測作者應該是一位在 macOS 下的慣用 Vim 的開發者。</p><h3 id="--3">三、察覺慣性</h3><p>在 macOS 下，我使用 <code>CTRL</code>+<code>Space</code> 來切換輸入法。ExDactyl 只有左邊姆指區有空白鍵這一點讓我非常不習慣，也因此使用不熟悉的 ExDactyl 後，讓我更了解自己，察覺自己習以為常的慣性動作，原來我只用右手姆指來按空白鍵，切換輸入法和輸出無蝦米的候選字。透過這樣的機會，可以調整自己不好的習慣或調整屬於自己習慣的 keymap 來提高效率。不過因為體驗期只有短短的一週，我決定偷懶，並沒有燒錄 firmware，就這樣適應著使用到最後。</p><h2 id="--4">最後</h2><p>比起 Model 01，我更喜歡它選擇的 Cherry MX 軸，但它在 Z 鍵那排下方又多了一排按鍵，左右共多出來的八顆鍵讓我按的很彆扭，我覺得這八顆鍵在有 layer 功能之下顯得有點多餘。雖然曲面的設計最終並沒有成為取代我現有鍵盤的理由，但 ExDactyl 對我的意義比起裝備更換這檔事其實更遠大，因為它替我開啟了一扇通往自造者鍵盤道路的窗。</p>]]></content:encoded></item><item><title><![CDATA[Elasticsearch 的 CLA]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>最近簽了 Elasticsearch 的 <a href="https://www.elastic.co/contributor-agreement">CLA</a>，它是利用 web form 輸入個人資料，並使用 Adobe EchoSign 的電子簽章技術簽名，送出後會寄出 PDF 的契約到你的信箱留存。以前這些條文都是匆匆一瞥，但這次過程有別以往，特別正式，讓我好奇葫蘆裡在賣什麼藥，為了保護自已的權益，該看的還是要看，免得被賣了還不自知。</p>
<p>這類智慧財產權的文件有很多專有術語，老實講並不容易理解，而此議題的相關文章也不多，幸好找到 OpenFoundry 有一篇<br>
<a href="https://www.openfoundry.org/tw/legal-column-list/8961-a-brief-introduciotn-of-the-application-of-contributor-agreement-in-the-field-of-foss">淺談貢獻者契約在自由開源軟體之應用</a>，值得一看的。</p>
<!--kg-card-end: markdown-->]]></description><link>https://blog.gasol.tw/elasticsearch-contributor-agreement/</link><guid isPermaLink="false">5e5f2a334e32c05f948a1807</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Mon, 01 Jun 2015 08:05:37 GMT</pubDate><media:content url="https://blog.gasol.tw/content/images/2021/01/53352581-b3892f80-392b-11e9-9532-5db5cbfc8f1c.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.gasol.tw/content/images/2021/01/53352581-b3892f80-392b-11e9-9532-5db5cbfc8f1c.jpg" alt="Elasticsearch 的 CLA"><p>最近簽了 Elasticsearch 的 <a href="https://www.elastic.co/contributor-agreement">CLA</a>，它是利用 web form 輸入個人資料，並使用 Adobe EchoSign 的電子簽章技術簽名，送出後會寄出 PDF 的契約到你的信箱留存。以前這些條文都是匆匆一瞥，但這次過程有別以往，特別正式，讓我好奇葫蘆裡在賣什麼藥，為了保護自已的權益，該看的還是要看，免得被賣了還不自知。</p>
<p>這類智慧財產權的文件有很多專有術語，老實講並不容易理解，而此議題的相關文章也不多，幸好找到 OpenFoundry 有一篇<br>
<a href="https://www.openfoundry.org/tw/legal-column-list/8961-a-brief-introduciotn-of-the-application-of-contributor-agreement-in-the-field-of-foss">淺談貢獻者契約在自由開源軟體之應用</a>，值得一看的。</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Filco 鍵盤換軸]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>這把 Filco 茶軸跟著我從唸書一直到成為職業鍵盤手，前陣子一個不小心把啤酒弄倒，結果經過兩三天後滲入的液體乾固，C 鍵因此手感儘失，敲打程式碼的體驗從此從天堂掉到地獄，幾乎是毀掉了這把好鍵盤。</p>
<p>為了一個 C 鍵換鍵盤實在太可惜了，於是又興起 DIY 換軸的念頭，同樣是 Cherry 的軸，用在不同廠牌的機構會有點不一樣，據我所知 Cherry 原廠鍵盤的軸就跟 Filco 的 Cherry 軸不一樣，所以購買時要特別注意這一點。</p>
<p>接下來就是重拾銲槍解鍚、上鍚，然後接上電測試一下所有接點，一切正常，收工！</p>
<p><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0746.jpg" alt="準備工具"><br>
準備工具</p>
<p><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0752.jpg" alt="解鍚"><br>
解鍚</p>
<p><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0751.jpg" alt="拿出解鍚後的軸"><br>
拿出解鍚後的軸</p>
<p><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0753.jpg" alt="上鍚後的新軸接點"><br>
上鍚後的新軸接點</p>
<p><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0754.jpg" alt="折解替換下來的軸，結構很簡單"><br>
折解替換下來的軸，結構簡單...</p>
<!--kg-card-end: markdown-->]]></description><link>https://blog.gasol.tw/change-cherry-mx-switch-on-filco-keyboard/</link><guid isPermaLink="false">5e5f2a334e32c05f948a1804</guid><category><![CDATA[ifixit]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Sat, 04 Apr 2015 22:43:50 GMT</pubDate><media:content url="https://blog.gasol.tw/content/images/2015/04/IMG_0754.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0754.jpg" alt="Filco 鍵盤換軸"><p>這把 Filco 茶軸跟著我從唸書一直到成為職業鍵盤手，前陣子一個不小心把啤酒弄倒，結果經過兩三天後滲入的液體乾固，C 鍵因此手感儘失，敲打程式碼的體驗從此從天堂掉到地獄，幾乎是毀掉了這把好鍵盤。</p>
<p>為了一個 C 鍵換鍵盤實在太可惜了，於是又興起 DIY 換軸的念頭，同樣是 Cherry 的軸，用在不同廠牌的機構會有點不一樣，據我所知 Cherry 原廠鍵盤的軸就跟 Filco 的 Cherry 軸不一樣，所以購買時要特別注意這一點。</p>
<p>接下來就是重拾銲槍解鍚、上鍚，然後接上電測試一下所有接點，一切正常，收工！</p>
<p><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0746.jpg" alt="Filco 鍵盤換軸"><br>
準備工具</p>
<p><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0752.jpg" alt="Filco 鍵盤換軸"><br>
解鍚</p>
<p><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0751.jpg" alt="Filco 鍵盤換軸"><br>
拿出解鍚後的軸</p>
<p><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0753.jpg" alt="Filco 鍵盤換軸"><br>
上鍚後的新軸接點</p>
<p><img src="https://blog.gasol.tw/content/images/2015/04/IMG_0754.jpg" alt="Filco 鍵盤換軸"><br>
折解替換下來的軸，結構簡單...</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[MX5021 檢修]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>主角是還在念書時買的 Altec Lansing MX5021 2.1 聲道電腦喇叭，好久前就已經發現它壞掉了，狀況是電源燈有亮，但是沒有聲音。最近幾年都是用 laptop 比較多，鮮少待在桌機前使用，所以就暫時把他晾在一旁。</p>
<p>過年時跟小仙女在整理房間時本來打算把它給丟了，結果用 KKBOX 透過 Chromecast 投放音樂在電視上時，發現電視內建的喇叭實在是很弱，平常都拿它來看政論節目，所以都沒什麼特別的感覺，於是就把腦筋動到它身上，自已拆修，死馬當活馬醫，修的好就省下一筆費用，搞不好只是像電容壞掉這類簡單的問題。</p>
<p>故事就這樣開始了，拆開來發現電容都沒有異狀，IC 也都沒有燒焦，電表也不知丟哪去了，毫無頭緒下，請出 Google 大神做最後的掙扎，利用搜尋的結果反覆修正關鍵字，最後找到 <code>mx5021</code> <code>黃膠</code> 這組，才發現是固定零件的黃膠惹的禍，網路上災情不少。</p>
<p><img src="https://blog.gasol.tw/content/images/2015/03/IMG_0392.JPG" alt="黃膠 1"></p>
<p><img src="https://blog.gasol.tw/content/images/2015/03/IMG_0394.JPG" alt="黃膠 2"></p>
<p><img src="https://blog.gasol.tw/content/images/2015/03/IMG_0395.JPG" alt="黃膠 3"></p>
<p>最後用一字起子兔了老半天，半信半疑的插上電源，結果就這樣神奇的復活了，這個 moment</p>]]></description><link>https://blog.gasol.tw/fix-mx5021/</link><guid isPermaLink="false">5e5f2a334e32c05f948a1800</guid><category><![CDATA[ifixit]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Tue, 03 Mar 2015 08:25:48 GMT</pubDate><media:content url="https://blog.gasol.tw/content/images/2015/03/IMG_0394.JPG" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.gasol.tw/content/images/2015/03/IMG_0394.JPG" alt="MX5021 檢修"><p>主角是還在念書時買的 Altec Lansing MX5021 2.1 聲道電腦喇叭，好久前就已經發現它壞掉了，狀況是電源燈有亮，但是沒有聲音。最近幾年都是用 laptop 比較多，鮮少待在桌機前使用，所以就暫時把他晾在一旁。</p>
<p>過年時跟小仙女在整理房間時本來打算把它給丟了，結果用 KKBOX 透過 Chromecast 投放音樂在電視上時，發現電視內建的喇叭實在是很弱，平常都拿它來看政論節目，所以都沒什麼特別的感覺，於是就把腦筋動到它身上，自已拆修，死馬當活馬醫，修的好就省下一筆費用，搞不好只是像電容壞掉這類簡單的問題。</p>
<p>故事就這樣開始了，拆開來發現電容都沒有異狀，IC 也都沒有燒焦，電表也不知丟哪去了，毫無頭緒下，請出 Google 大神做最後的掙扎，利用搜尋的結果反覆修正關鍵字，最後找到 <code>mx5021</code> <code>黃膠</code> 這組，才發現是固定零件的黃膠惹的禍，網路上災情不少。</p>
<p><img src="https://blog.gasol.tw/content/images/2015/03/IMG_0392.JPG" alt="MX5021 檢修"></p>
<p><img src="https://blog.gasol.tw/content/images/2015/03/IMG_0394.JPG" alt="MX5021 檢修"></p>
<p><img src="https://blog.gasol.tw/content/images/2015/03/IMG_0395.JPG" alt="MX5021 檢修"></p>
<p>最後用一字起子兔了老半天，半信半疑的插上電源，結果就這樣神奇的復活了，這個 moment 比剛買時還開心啊...XD</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Cannot use strict as namespace in PHP]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Because strict is reserved to be joke, I accidentally found this easter egg in php-src <a href="https://github.com/php/php-src">1</a> some day.</p>
<p>WTF...XD</p>
<pre><code>$ php -r 'use strict;'
PHP Fatal error:  You seem to be trying to use a different language... in Command line code on line 1
</code></pre>
<!--kg-card-end: markdown-->]]></description><link>https://blog.gasol.tw/cannot-use-strict-as-namespace-in-php/</link><guid isPermaLink="false">5e5f2a334e32c05f948a17fe</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Wed, 03 Dec 2014 11:30:13 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Because strict is reserved to be joke, I accidentally found this easter egg in php-src <a href="https://github.com/php/php-src">1</a> some day.</p>
<p>WTF...XD</p>
<pre><code>$ php -r 'use strict;'
PHP Fatal error:  You seem to be trying to use a different language... in Command line code on line 1
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[PHP logo 彩蛋]]></title><description><![CDATA[<!--kg-card-begin: markdown--><img src="http://php.net/images/logos/elephpant-running-78x48.gif">
<p>PHP 的吉祥物是大象，這個大家都不陌生，但是如果看到一隻飛奔的大象，那你可能會揉揉眼睛質疑自已是否眼花。</p>
<p>事實上這是 PHP 官方所埋藏的彩蛋，因為有人運氣好無意間看到，但不太領情，反而覺得不舒服，所以跑到 PHP mailing list 抱怨 <a href="http://news.php.net/php.webmaster/20208">1</a> 才被挖掘出來...</p>
<p>除此之外，在 <a href="https://php.net">https://php.net</a> 任意網頁下直接鍵入 logo 然後按 enter 後，還有很多復古的 logo 等著大家去發現，可以參閱 Github 上的程式碼 <code>logo.php</code> <a href="https://github.com/php/web-php/blob/master/images/logo.php">2</a> 和 <code>common.js</code> <a href="https://github.com/php/web-php/blob/master/js/common.js">3</a>。</p>
<!--kg-card-end: markdown-->]]></description><link>https://blog.gasol.tw/easter-egg-of-php-logo/</link><guid isPermaLink="false">5e5f2a334e32c05f948a17fd</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Wed, 03 Dec 2014 11:13:54 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="http://php.net/images/logos/elephpant-running-78x48.gif">
<p>PHP 的吉祥物是大象，這個大家都不陌生，但是如果看到一隻飛奔的大象，那你可能會揉揉眼睛質疑自已是否眼花。</p>
<p>事實上這是 PHP 官方所埋藏的彩蛋，因為有人運氣好無意間看到，但不太領情，反而覺得不舒服，所以跑到 PHP mailing list 抱怨 <a href="http://news.php.net/php.webmaster/20208">1</a> 才被挖掘出來...</p>
<p>除此之外，在 <a href="https://php.net">https://php.net</a> 任意網頁下直接鍵入 logo 然後按 enter 後，還有很多復古的 logo 等著大家去發現，可以參閱 Github 上的程式碼 <code>logo.php</code> <a href="https://github.com/php/web-php/blob/master/images/logo.php">2</a> 和 <code>common.js</code> <a href="https://github.com/php/web-php/blob/master/js/common.js">3</a>。</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[PHP 執行期動態產生 class instance]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>今天在聽 <a href="http://www.jaceju.net">小鐵</a> 講 DI 實作 <a href="http://www.jaceju.net/blog/archives/php-di-container/">1</a> 時提到了這個例子，當下在想如果換成 PHP 5.6 的 arguments unpack 的語法不曉得會不會動，驗證完後覺得很有趣，順便來寫一篇。</p>
<p>不像 Java，PHP 在執行期時才決定要產生哪個類別的 instance 或呼叫哪個函式，並不需要大費周章的用 Reflection API，但還是有些是不透過 Reflection 辦不到的事，其中產生 instance 時如果要傳入參數的話，就需要透過 <a href="http://php.net/manual/en/class.reflectionclass.php">ReflectionClass</a> 來達成這個目的。</p>
<p>前面提到的問題，你沒辦法用 <code>call_user_func</code> 或  <code>call_user_func_array</code> 來達成目的，因為沒辦法滿足第一個 callable 參數...</p>
<ol>
<li>
<p>建構子不是</p></li></ol>]]></description><link>https://blog.gasol.tw/create-dynamic-class-programmatically-in-php/</link><guid isPermaLink="false">5e5f2a334e32c05f948a17f6</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Thu, 28 Aug 2014 11:28:24 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>今天在聽 <a href="http://www.jaceju.net">小鐵</a> 講 DI 實作 <a href="http://www.jaceju.net/blog/archives/php-di-container/">1</a> 時提到了這個例子，當下在想如果換成 PHP 5.6 的 arguments unpack 的語法不曉得會不會動，驗證完後覺得很有趣，順便來寫一篇。</p>
<p>不像 Java，PHP 在執行期時才決定要產生哪個類別的 instance 或呼叫哪個函式，並不需要大費周章的用 Reflection API，但還是有些是不透過 Reflection 辦不到的事，其中產生 instance 時如果要傳入參數的話，就需要透過 <a href="http://php.net/manual/en/class.reflectionclass.php">ReflectionClass</a> 來達成這個目的。</p>
<p>前面提到的問題，你沒辦法用 <code>call_user_func</code> 或  <code>call_user_func_array</code> 來達成目的，因為沒辦法滿足第一個 callable 參數...</p>
<ol>
<li>
<p>建構子不是 static method，你沒辦法用 <code>Foo::__construct</code> 這種方式，如果可以的話，參數的傳遞是另外一個問題。</p>
</li>
<li>
<p>在執行建構子時你需要傳入 instance，你都要靠它產生 instance 了，它還跟你討 instance，這兩個條件互斥了，變成雞生蛋與蛋生雞的問題，所以沒辦法用 <code>array($instance, '__construct')</code>。</p>
</li>
</ol>
<p>PoC 的程式碼如下，跑一下 benchmark</p>
<pre><code class="language-language-php">&lt;?php
// bench.php
function bench($callback)
{
    $start = microtime(true);
    $callback();
    return (microtime(true) - $start);
}

class Foo
{
    public function __construct($a, $b, $c)
    {
        // var_dump(func_get_args());
    }
}

$class_name = 'Foo';
$loop = 5000000;

$elapsed_time = bench(
    function() use ($class_name, $loop) {
        global $argv;

        $i = 0;
        while ($i++ &lt; $loop) {
            $ref_class = new ReflectionClass($class_name);
            $instance = $ref_class-&gt;newInstanceArgs($argv);
        }
    }
);
echo &quot;Reflection take $elapsed_time seconds\n&quot;;

$elapsed_time = bench(
    function() use ($class_name, $loop) {
        global $argv;

        $i = 0;
        while ($i++ &lt; $loop) {
            $instance = new $class_name(...$argv);
        }
    }
);
echo &quot;Arguments unpack take $elapsed_time seconds\n&quot;;
</code></pre>
<p>輸出如下，可以看出效率快一點點，另外的優點是拿 splat operator <code>...</code> 來換兩行 ReflectionClass 的寫法變的簡潔有力！</p>
<pre><code>shell&gt; time ~/.phpbrew/php/php-5.6.0RC4/bin/php bench.php foo bar
Reflection take 8.2250480651855 seconds
Arguments unpack take 2.4252769947052 seconds

real    0m10.669s
user    0m10.656s
sys     0m0.008s
</code></pre>
<p>提外話：</p>
<p>在過程中還發生另外的小插曲，在利用 phpbrew build 5.6.0RC4 來測試上面的 PoC，發現在 build unstable 的版本時動不了，動手修正後送 pull request <a href="https://github.com/phpbrew/phpbrew/pull/294">2</a> 回去，不久後收到 comment 的回覆的信件時，順便被告知 PHP 5.6.0 GA 了！！也太巧了吧...XD</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[建立自已的 homebrew]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>GitHub 作為地表上最受歡迎原始碼代管平台 <a href="https://en.wikipedia.org/wiki/Comparison_of_open-source_software_hosting_facilities#Popularity">1</a>，自然而然就會有工具利用它形成一個分享的生態圈，<a href="https://github.com/gmarik/Vundle.vim">Vundle</a> 及 <a href="http://brew.sh">Homebrew</a> 就是經典的例子。</p>
<p>用 Vundle 安裝 vim plugin 時，只要簡單像下面加上一行 Plugin directive。</p>
<pre><code>&quot; .vimrc
Plugin &quot;user/repo&quot;
</code></pre>
<p>在 <code>:PluginInstall</code> 時就會去 GitHub 把 user/repo clone 在 <code>.vim/bundle/user-repo</code> 裡，再利用修改 vim <code>runtimepath</code> 的方法達到載入 plugin 的目的。</p>
<p>今天要介紹的 Homebrew Tap <a href="https://github.com/Homebrew/homebrew/wiki/brew-tap">2</a> 也是利用 GitHub 來新增額外的</p>]]></description><link>https://blog.gasol.tw/make-your-own-homebrew/</link><guid isPermaLink="false">5e5f2a334e32c05f948a17e5</guid><category><![CDATA[Package Management System]]></category><category><![CDATA[Homebrew]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Tue, 26 Aug 2014 01:41:40 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>GitHub 作為地表上最受歡迎原始碼代管平台 <a href="https://en.wikipedia.org/wiki/Comparison_of_open-source_software_hosting_facilities#Popularity">1</a>，自然而然就會有工具利用它形成一個分享的生態圈，<a href="https://github.com/gmarik/Vundle.vim">Vundle</a> 及 <a href="http://brew.sh">Homebrew</a> 就是經典的例子。</p>
<p>用 Vundle 安裝 vim plugin 時，只要簡單像下面加上一行 Plugin directive。</p>
<pre><code>&quot; .vimrc
Plugin &quot;user/repo&quot;
</code></pre>
<p>在 <code>:PluginInstall</code> 時就會去 GitHub 把 user/repo clone 在 <code>.vim/bundle/user-repo</code> 裡，再利用修改 vim <code>runtimepath</code> 的方法達到載入 plugin 的目的。</p>
<p>今天要介紹的 Homebrew Tap <a href="https://github.com/Homebrew/homebrew/wiki/brew-tap">2</a> 也是利用 GitHub 來新增額外的 repo，用來分類與管理 formulae。</p>
<p>舉我剛剛建立的 <a href="https://github.com/Gasol/homebrew-formulae">Gasol/homebrew-formulae</a> 為例：</p>
<p>首先在 GitHub 上建立好 repository，這邊要注意的是 repo 的名字一定要有 <code>homebrew-</code> 的前綴 <a href="https://github.com/Homebrew/homebrew/blob/4525bd4719b9451e6c9a77021d0d1b946221f976/Library/Homebrew/cmd/tap.rb#L20">3</a>，否則 <code>brew tap</code> 會找不到。</p>
<pre><code>shell&gt; brew tap gasol/formulae
</code></pre>
<p><code>brew tap</code> 會把 repo clone 在 <code>/usr/local/Library/Taps/gasol/homebrew-formulae</code></p>
<pre><code>shell&gt; tree -CL 2 /usr/local/Library/Taps/
/usr/local/Library/Taps/
├── caskroom
│   └── homebrew-cask
├── gasol
│   └── homebrew-formulae
└── homebrew
├── homebrew-completions
├── homebrew-dupes
├── homebrew-php
└── homebrew-science
</code></pre>
<p>為了要 push formulae 上去 remote，所以我們要把 git 的 url 從 https 換成 ssh 的版本。</p>
<pre><code>
shell&gt; cd /usr/local/Library/Taps/gasol/homebrew-formulae
shell /usr/local/Library/Taps/gasol/homebrew-formulae&gt; git remote show origin
* remote origin
  Fetch URL: https://github.com/Gasol/homebrew-formulae.git
  Push  URL: https://github.com/Gasol/homebrew-formulae.git
  HEAD branch: master
  Remote branch:
    master tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

shell /usr/local/Library/Taps/gasol/homebrew-formulae&gt; git remote set-url origin git@github.com:Gasol/homebrew-formulae.git
shell /usr/local/Library/Taps/gasol/homebrew-formulae&gt; git remote show origin
* remote origin
  Fetch URL: git@github.com:Gasol/homebrew-formulae.git
  Push  URL: git@github.com:Gasol/homebrew-formulae.git
  HEAD branch: master
  Remote branch:
    master tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

</code></pre>
<p>之後就可以把自已建立的 formulae commit 進 repo 後再 push 上 GitHub。</p>
<pre><code>shell /usr/local/Library/Taps/gasol/homebrew-formulae&gt; git push -u origin master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 485 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:Gasol/homebrew-formulae.git
 * [new branch]      master -&gt; master
Branch master set up to track remote branch master from origin.
</code></pre>
<p>舉凡官方不收或是有 license 疑慮的 formulae 變可透過 Homebrew Tap 的方式來自已管理。</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[PHP 使用 feof 該注意的事]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>相對於其他語言，PHP 對於曾經寫過 C 的人來說比較容易上手，其中一個原因是 PHP 與 Standard C Library (libc) 有眾多函式名稱是一樣的，我相信 PHP 是刻意如此，有些函式連參數也一樣，只存在強弱型別的差異而已，檔案操作一系列的函式 <code>fopen</code>、<code>fread</code>、<code>fwrite</code>、<code>fclose</code>、<code>feof</code>、<code>fgets</code>、<code>fgetc</code>、<code>ftell</code>、<code>fseek</code>...等等就是較經典的例子。</p>
<p>其中 <code>feof</code> 就是今天的主角，使用上有一些眉角要注意，<code>feof</code> 通常拿來確認有沒有完整讀取到檔尾，所以在 PHP 中如果要從 stdin 一行一行的讀出來做資料處理的話可以寫成下面這樣：</p>
<pre><code class="language-language-php">&lt;?php
// feof.php
while (!feof(STDIN)) {
    $line = fgets(STDIN)</code></pre>]]></description><link>https://blog.gasol.tw/php-feof-notes/</link><guid isPermaLink="false">5e5f2a334e32c05f948a17f2</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Thu, 31 Jul 2014 11:54:20 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>相對於其他語言，PHP 對於曾經寫過 C 的人來說比較容易上手，其中一個原因是 PHP 與 Standard C Library (libc) 有眾多函式名稱是一樣的，我相信 PHP 是刻意如此，有些函式連參數也一樣，只存在強弱型別的差異而已，檔案操作一系列的函式 <code>fopen</code>、<code>fread</code>、<code>fwrite</code>、<code>fclose</code>、<code>feof</code>、<code>fgets</code>、<code>fgetc</code>、<code>ftell</code>、<code>fseek</code>...等等就是較經典的例子。</p>
<p>其中 <code>feof</code> 就是今天的主角，使用上有一些眉角要注意，<code>feof</code> 通常拿來確認有沒有完整讀取到檔尾，所以在 PHP 中如果要從 stdin 一行一行的讀出來做資料處理的話可以寫成下面這樣：</p>
<pre><code class="language-language-php">&lt;?php
// feof.php
while (!feof(STDIN)) {
    $line = fgets(STDIN);
    var_dump($line);
}
</code></pre>
<p>執行結果輸出如下，你會發現多出了 <code>false</code> 這個預期外的結果...</p>
<pre><code>shell&gt; echo -e &quot;1\n2\n3&quot; | php feof.php
string(2) &quot;1
&quot;
string(2) &quot;2
&quot;
string(2) &quot;3
&quot;
bool(false)
</code></pre>
<p>原因是 <code>feof</code> 是被動方式確認 flag 是否包含 EOF，所以要有另外一個角色要主動來設 EOF flag，這個主動的方式就是透過讀取的行為觸發，這點在 C 裡行為也是一樣的，所以安全起見可以多加一行判斷改寫成這樣，多出來的那一行 <code>false</code> 就不見了。</p>
<pre><code class="language-language-php">&lt;?php
// feof.php
while (!feof(STDIN)) {
    $line = fgets(STDIN);
    if (feof(STDIN)) {
        break;
    }
    var_dump($line);
}
</code></pre>
<p>上面會動，但是不夠，因為太醜了，<code>fgets</code> 回傳 <code>false</code> 代表兩個意思，一是沒有資料可以讀取，二是有錯誤產生，利用這個特性再改寫如下：</p>
<pre><code class="language-language-php">&lt;?php
// feof.php
while (!feof(STDIN)) {
    $line = fgets(STDIN);
    if (false === $line) {
        break; // continue is bad.
    }
    var_dump($line);
}
</code></pre>
<p>上述程式碼變數 $line 遇到 <code>false</code> 後 <code>break</code> 跳出迴圈，如果 <code>false</code> 是代表一的狀況的話算是瞎貓碰上死耗子，如果是第二種狀況，那就有讀取不完整的問題。如果把 <code>break</code> 用 <code>continue</code> 取代，意味著 <code>fgets</code> 遇到錯誤重新嘗試 (retry)，判斷檔案結束由 <code>feof</code> 把關，這樣同時解決了兩個的問題，但是如果碰上 <code>fgets</code> 一直失敗這種 edge case 就會造成無窮迴圈的產生，應該要儘量避免...</p>
<p>下面的版本充份利用 <code>fgets</code> 及 <code>feof</code> 的特性：</p>
<pre><code class="language-language-php">&lt;?php
// feof.php
while (true) {
    $line = fgets(STDIN);
    if (false === $line) {
        break;
    }
    var_dump($line);
}

if (!feof(STDIN)) {
    trigger_error(&quot;Failed to read&quot;, E_USER_WARNING);
}
</code></pre>
<p>大量的讀檔可以稍微快一點的版本，犧牲一點可讀性換來一點效率...</p>
<pre><code class="language-language-php">&lt;?php
// feof.php
while (false !== ($line = fgets(STDIN))) {
    var_dump($line);
}

if (!feof(STDIN)) {
    trigger_error(&quot;Failed to read&quot;, E_USER_WARNING);
}
</code></pre>
<p>到這邊還沒結束，<code>feof</code> 參數是 stream resource，如果參數不對會有 warning 警告，但回傳值是 <code>false</code>，這很容易造成無窮迴圈，所以如果你沒有檢查回傳值是否正確的好習慣，預期 <code>fopen</code> 開檔都會成功回傳 file pointer，沒有檢查 <code>fopen</code> 開檔失敗，就很容易踩中這個地雷。</p>
<pre><code class="language-language-php">&lt;?php
// infinite loop demonstration
$fp = fopen('/file/not_exist', 'r');
while (!feof($fp)) {
    var_dump($line);
}
</code></pre>
<p>另外 <code>feof</code> 拿來處理 <code>fsockopen</code> 或是 <code>fopen</code> HTTP 的 url 回傳的 stream 也很危險，官網上 workaround <a href="http://php.net/manual/en/function.feof.php#refsect1-function.feof-notes">1</a> 的方法只能說醜到爆炸，所以如果有 HTTP 的操作請儘量用高階一點的 API 來取代。</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[tmux 另開視窗繼承工作目錄]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>以前沒有特別注意到的功能，<a href="https://twitter.com/AaronLin10">Aaron</a> 說 tmux 更新成 1.9 就不會動了，官方 <a href="http://tmux.svn.sourceforge.net/viewvc/tmux/trunk/FAQ">FAQ - How can I open a new window in the same directory as the current window?</a> 提供了一個方法，但是設上去了也沒有效。</p>
<p>官方的方法 <code>TMUXPWD_$(tmux display -p &quot;#I&quot;)</code> 是把當下的工作目錄存在環境變數，<code>#I</code> 為 Index of Window，然後 bind 一組按鍵來執行 <code>tmux new-window</code> 指令，可以簡寫成 <code>neww</code></p>]]></description><link>https://blog.gasol.tw/tmux-new-window-with-previous-working-directory/</link><guid isPermaLink="false">5e5f2a334e32c05f948a17e9</guid><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Wed, 02 Jul 2014 08:54:07 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>以前沒有特別注意到的功能，<a href="https://twitter.com/AaronLin10">Aaron</a> 說 tmux 更新成 1.9 就不會動了，官方 <a href="http://tmux.svn.sourceforge.net/viewvc/tmux/trunk/FAQ">FAQ - How can I open a new window in the same directory as the current window?</a> 提供了一個方法，但是設上去了也沒有效。</p>
<p>官方的方法 <code>TMUXPWD_$(tmux display -p &quot;#I&quot;)</code> 是把當下的工作目錄存在環境變數，<code>#I</code> 為 Index of Window，然後 bind 一組按鍵來執行 <code>tmux new-window</code> 指令，可以簡寫成 <code>neww</code>，該指令後面可以帶 shell-command，如此，就可以在開新視窗時切換到預先存在環境變數的工作目錄。</p>
<p>這個麻煩的方法不能動的原因在於 $PWD 的 precedence，其實可以用 <code>PROMPT_COMMAND</code> 來修正，不過緊接著提供的方法更簡潔。</p>
<pre><code>PROMPT_COMMAND=$([ -n &quot;$TMUX&quot; ] &amp;&amp; tmux setenv TMUXPWD_$(tmux display -p &quot;#I&quot;) $PWD)
</code></pre>
<p>tmux 1.9 之前是照著下面這個 commit 的說明運作，<code>default-path</code> 設定來決定另開新窗的預設工作目錄，如果沒有設定，預設就是繼承上一個視窗的工作目錄。</p>
<pre><code>c1b994852594b23b7443e01e05257c991684ba4e -p
commit c1b994852594b23b7443e01e05257c991684ba4e
Author: Nicholas Marriott &lt;nicholas.marriott@gmail.com&gt;
Date:   Fri Dec 9 16:37:29 2011 +0000

    Change the way the working directory for new processes is discovered. If
    default-path isn't empty, it is used. Otherwise:

    1) If tmux neww is run from the command line, the working directory of the
    client is used.

    2) Otherwise use some platform specific code to retrieve the current working
    directory of the process in the active pane.

    3) If that fails, the directory where the session was created is used.

    Idea and support code, Linux, Solaris, FreeBSD bits by Romain Francoise,
    OpenBSD bits by me.
</code></pre>
<p>不過 <code>default-path</code> 在 1.9 時被移除了，見下面 commit，取而帶之是用 <code>-c</code> 的參數來決定 <code>new</code>、<code>neww</code>、<code>splitw</code> 開新視窗時的工作目錄。</p>
<pre><code>commit 4538c269d0b366a770a5a5ebfe0c5007569edbc1
Author: Nicholas Marriott &lt;nicholas.marriott@gmail.com&gt;
Date:   Sun Oct 6 21:02:23 2013 +0100

    Alter how tmux handles the working directory to internally use file descriptors
    rather than strings.

    - Each session still has a current working directory.

    - New sessions still get their working directory from the client that created
      them or its attached session if any.

    - New windows are created by default in the session working directory.

    - The -c flag to new, neww, splitw allows the working directory to be
      overridden.

    - The -c flag to attach let's the session working directory be changed.

    - The default-path option has been removed.

    To get the equivalent to default-path '.', do:

            bind c neww -c $PWD

    To get the equivalent of default-path '', do:

            bind c neww -c '#{pane_current_path}'

    The equivalent of default-path '~' is left as an exercise for the reader.

    This also changes the client identify protocol to be a set of messages rather
    than one as well as some other changes that should make it easier to make
    backwards-compatible protocol changes in future.
</code></pre>
<p>最後我們把 escape + CTRL-c 的組合鍵定義成開新視窗時保留當下的工作目錄的動作。</p>
<pre><code>// .tmux.conf
bind-key C-c new-window -c '#{pane_current_path}'
</code></pre>
<p>有沒有注意到些什麼？ tmux commit message 寫的好漂亮...</p>
<p>Happy ending.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[小烏龜畫 MRTG 流量圖]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>下圖是家裡的流量圖，零晨到二點半那段是在看 Google IO 直播產生的流量，挺有趣的...:)，不過 In/Out 相反了，後面有談到解決方法。</p>
<p><img src="https://blog.gasol.tw/content/images/2014/Jun/Screen-Shot-2014-06-26-at-11-46-22-PM.png" alt></p>
<p>小烏龜就是申請中華電信網路服務時所配發的 vdsl modem，我拿到的型號是 ZyXEL P874，有簡易的 Web 介面可以透過瀏覽器設定，即時在遠端也可以透過 telnet 來作業。</p>
<pre><code>$ telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.
ZyXEL VDSL Router
Login: cht
Password:</code></pre>]]></description><link>https://blog.gasol.tw/make-mrtg-graph-for-home-router/</link><guid isPermaLink="false">5e5f2a334e32c05f948a17e7</guid><category><![CDATA[System Administration]]></category><category><![CDATA[Network]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Thu, 26 Jun 2014 08:33:15 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>下圖是家裡的流量圖，零晨到二點半那段是在看 Google IO 直播產生的流量，挺有趣的...:)，不過 In/Out 相反了，後面有談到解決方法。</p>
<p><img src="https://blog.gasol.tw/content/images/2014/Jun/Screen-Shot-2014-06-26-at-11-46-22-PM.png" alt></p>
<p>小烏龜就是申請中華電信網路服務時所配發的 vdsl modem，我拿到的型號是 ZyXEL P874，有簡易的 Web 介面可以透過瀏覽器設定，即時在遠端也可以透過 telnet 來作業。</p>
<pre><code>$ telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.
ZyXEL VDSL Router
Login: cht
Password: &lt;google&gt;

   Main Menu

 1. VDSL Link State
 2. LAN/WLAN
 3. WAN
 4. DNS Server
 5. Route Setup
 6. NAT
 7. Firewall
 8. Quality Of Service
 9. Management
10. Passwords
11. Reset to Default
12. Reboot
13. Exit
 &gt; 9
 
   SNMP Agent Menu

 1. Configure
 2. Show
 3. Exit
/Management/SNMP Agent &gt; 2

        SNMP Agent Information

State                : enable
Read only community  : chtnvdsl
Read write community : xdsl
System name          : CHT
System location      : unknown
System contact       : unknown
Trap IP address      : 0.0.0.0

Hit &lt;enter&gt; to continue
</code></pre>
<p>預設 SNMP 是開啟的，可以看到 Read only cummunity 的值是 <code>chtnvdsl</code> (咦！怎麼有點眼熟...XD)，接著用 <code>snmpwalk</code> 來看一下總共有哪些 OID。</p>
<pre><code>$ snmpwalk -c chtnvdsl 192.168.1.1
IF-MIB::ifNumber.0 = INTEGER: 14
IF-MIB::ifIndex.1 = INTEGER: 1
IF-MIB::ifIndex.2 = INTEGER: 2
IF-MIB::ifIndex.3 = INTEGER: 3
IF-MIB::ifIndex.4 = INTEGER: 4
IF-MIB::ifIndex.5 = INTEGER: 5
IF-MIB::ifIndex.6 = INTEGER: 6
IF-MIB::ifIndex.7 = INTEGER: 7
IF-MIB::ifIndex.8 = INTEGER: 8
IF-MIB::ifIndex.9 = INTEGER: 9
IF-MIB::ifIndex.10 = INTEGER: 10
IF-MIB::ifIndex.11 = INTEGER: 11
IF-MIB::ifIndex.12 = INTEGER: 12
IF-MIB::ifIndex.13 = INTEGER: 13
IF-MIB::ifIndex.14 = INTEGER: 14
IF-MIB::ifDescr.1 = STRING: eth0
IF-MIB::ifDescr.2 = STRING: eth1
IF-MIB::ifDescr.3 = STRING: eth2
IF-MIB::ifDescr.4 = STRING: eth3
IF-MIB::ifDescr.5 = STRING: wl0
IF-MIB::ifDescr.6 = STRING: wl0.1
IF-MIB::ifDescr.7 = STRING: wl0.2
IF-MIB::ifDescr.8 = STRING: wl0.3
IF-MIB::ifDescr.9 = STRING: ptm0
IF-MIB::ifDescr.10 = STRING: ptm0_1
IF-MIB::ifDescr.11 = STRING: ptm0_2
IF-MIB::ifDescr.12 = STRING: ptm0_4
IF-MIB::ifDescr.13 = STRING: ppp0_3
IF-MIB::ifDescr.14 = STRING: dsl0
... skip
</code></pre>
<p>確認完後就用 <code>cfgmaker</code> 產生設定檔樣版。</p>
<pre><code>$ cfgmaker --output=mrtg.cfg chtnvdsl@192.168.1.1
</code></pre>
<p>接著修改設定，沒有 ifSpeed 的值和 ifOperStatus 為 down 時設定會是註解起來的，然後介面 <code>ppp0_3</code> 的 In/Out 是相反的，參考 MRTG Reference - Reversing <a href="http://oss.oetiker.ch/mrtg/doc/mrtg-reference.en.html#Target_CONTENT">1</a>，把 Target 的值多個負號 (-) 就可以了</p>
<p>範例</p>
<pre><code>Target[ezci]: -1:public@ezci-ether.domain
</code></pre>
<p>接著用 <code>mrtg</code> 透過 RRDTool 來生流量圖，這個指令要定時跑才能定時更新流量圖，所以要把這個指令加入 cron 每五分鐘值行一次，Mac 可以透過 launchd 來排程。</p>
<pre><code>$ env LANG=C mrtg /etc/mrtg.cfg
</code></pre>
<p>用 <code>indexmaker</code> 產生 HTML 並擺在 Web 存取的到的地方，這邊只要執行一次，要注意輸出的目錄權限能不能寫入。</p>
<pre><code>$ indexmaker --output=/path/to/htdocs/mrtg/index.html --title=Home mrtg.cfg
</code></pre>
<p>這樣就完成了，等個 24 小時應該就有些數據可以看了。</p>
<p>在做之前其實有 survey 了其他的方案，比較過後最後還是覺得用 MRTG 簡單畫就好了，相依性少一點，如果要偵測異常流量的話只要定個 threshold，然後簡單寫個 shell script 排程跑寄信通知就好了。</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Git repository 一分為二的做法]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>活著 (active) 的專案隨著時間過去只會愈長愈大，豬養肥了就是要殺? 不管理由如何，總是有重整的機會。一般的情況下，目錄結構都是有組織的，也就是同性質的 resource 會擺放在一起，所以大部份的 case 都是拆單一目錄出來為獨立的 git repository。</p>
<p>依目錄拆 repository，換句話說就是把該目錄的 commit 全部保留，其餘的不要，當你建出這一個分支時，剩下的就只是合併剩下的成果而已，只要掌握這個概念的話，下面的操作就不難理解。</p>
<p>一開始我的土炮做法，以 laravel/framework <a href="https://github.com/laravel/framework">1</a> 為例子，把 <code>illuminate/database</code> 拆出來。</p>
<p>首先做不做備份取決於你的信心，因為 remote 那有一份了，所以我自已都沒有做。<br>
你可以另外 clone 一份 repository 下來，也可以另外開 branch 出來操作。</p>
<pre><code>$ git</code></pre>]]></description><link>https://blog.gasol.tw/splitting-a-git-repository-in-two/</link><guid isPermaLink="false">5e5f2a334e32c05f948a17e3</guid><category><![CDATA[Git]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Mon, 16 Jun 2014 10:32:21 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>活著 (active) 的專案隨著時間過去只會愈長愈大，豬養肥了就是要殺? 不管理由如何，總是有重整的機會。一般的情況下，目錄結構都是有組織的，也就是同性質的 resource 會擺放在一起，所以大部份的 case 都是拆單一目錄出來為獨立的 git repository。</p>
<p>依目錄拆 repository，換句話說就是把該目錄的 commit 全部保留，其餘的不要，當你建出這一個分支時，剩下的就只是合併剩下的成果而已，只要掌握這個概念的話，下面的操作就不難理解。</p>
<p>一開始我的土炮做法，以 laravel/framework <a href="https://github.com/laravel/framework">1</a> 為例子，把 <code>illuminate/database</code> 拆出來。</p>
<p>首先做不做備份取決於你的信心，因為 remote 那有一份了，所以我自已都沒有做。<br>
你可以另外 clone 一份 repository 下來，也可以另外開 branch 出來操作。</p>
<pre><code>$ git clone git@github.com:laravel/framework.git
</code></pre>
<p>開一個 database branch 來操作</p>
<pre><code>$ cd framework
framework (master) $ git checkout -b database
framework (database) $ 
</code></pre>
<p>利用 git <code>filter-branch</code> 來改寫歷史</p>
<pre><code>framework (database) $ git filter-branch --subdirectory-filter src/Illuminate/Database/ database
Rewrite adbb29172a4a70972c15e9564649dd4b520fcf66 (1059/1059)
Ref 'refs/heads/database' was rewritten
</code></pre>
<p>結束後可以看到 src/Illuminate/Database 目錄下的檔案都攤在根目錄了</p>
<pre><code>framework (database) $ ls -l
total 136
drwxrwxr-x 10 gasolwu gasolwu  4096  6月 17 01:35 ./
drwxrwxr-x 10 gasolwu gasolwu  4096  6月 17 01:28 ../
drwxrwxr-x  2 gasolwu gasolwu  4096  6月 17 01:35 Capsule/
-rwxrwxr-x  1 gasolwu gasolwu   889  6月 17 01:35 composer.json*
-rwxrwxr-x  1 gasolwu gasolwu  2895  6月 17 01:35 ConnectionInterface.php*
-rwxrwxr-x  1 gasolwu gasolwu 20643  6月 17 01:35 Connection.php*
-rwxrwxr-x  1 gasolwu gasolwu   502  6月 17 01:35 ConnectionResolverInterface.php*
-rwxrwxr-x  1 gasolwu gasolwu  1615  6月 17 01:35 ConnectionResolver.php*
drwxrwxr-x  2 gasolwu gasolwu  4096  6月 17 01:35 Connectors/
drwxrwxr-x  3 gasolwu gasolwu  4096  6月 17 01:35 Console/
-rwxrwxr-x  1 gasolwu gasolwu  6141  6月 17 01:35 DatabaseManager.php*
-rwxrwxr-x  1 gasolwu gasolwu  1267  6月 17 01:35 DatabaseServiceProvider.php*
drwxrwxr-x  3 gasolwu gasolwu  4096  6月 17 01:35 Eloquent/
drwxrwxr-x  8 gasolwu gasolwu  4096  6月 17 01:35 .git/
-rwxrwxr-x  1 gasolwu gasolwu  3611  6月 17 01:35 Grammar.php*
drwxrwxr-x  3 gasolwu gasolwu  4096  6月 17 01:35 Migrations/
-rwxrwxr-x  1 gasolwu gasolwu  5537  6月 17 01:35 MigrationServiceProvider.php*
-rwxrwxr-x  1 gasolwu gasolwu  1444  6月 17 01:35 MySqlConnection.php*
-rwxrwxr-x  1 gasolwu gasolwu  1194  6月 17 01:35 PostgresConnection.php*
drwxrwxr-x  4 gasolwu gasolwu  4096  6月 17 01:35 Query/
-rw-rw-r--  1 gasolwu gasolwu  1340  6月 17 01:35 QueryException.php
-rwxrwxr-x  1 gasolwu gasolwu  2198  6月 17 01:35 README.md*
drwxrwxr-x  3 gasolwu gasolwu  4096  6月 17 01:35 Schema/
-rwxrwxr-x  1 gasolwu gasolwu  1661  6月 17 01:35 Seeder.php*
-rwxrwxr-x  1 gasolwu gasolwu   975  6月 17 01:35 SeedServiceProvider.php*
-rwxrwxr-x  1 gasolwu gasolwu  1133  6月 17 01:35 SQLiteConnection.php*
-rwxrwxr-x  1 gasolwu gasolwu  2122  6月 17 01:35 SqlServerConnection.php*
</code></pre>
<p>這時後也可以用 <code>tig</code> 或 <code>git log</code> 看一下 history 長什麼樣子。</p>
<pre><code>framework (database) $ git log -m -stat -n 1
commit 291c2e9139086aefefb3db32e56675c67d92c1ec (from ec71e594c245eeb5c05d6de7a770399e045341a4)
Merge: ec71e59 74b8089
Author: Taylor Otwell &lt;taylorotwell@gmail.com&gt;
Date:   Fri Jun 13 15:12:36 2014 -0500

    Merge branch '4.2'

 Console/Migrations/RefreshCommand.php | 10 ++++++++--
 Schema/Blueprint.php                  | 10 ++++++++++
 2 files changed, 18 insertions(+), 2 deletions(-)

commit 291c2e9139086aefefb3db32e56675c67d92c1ec (from 74b8089b6ff5aed2129cbd7e7559144b68d3f660)
Merge: ec71e59 74b8089
Author: Taylor Otwell &lt;taylorotwell@gmail.com&gt;
Date:   Fri Jun 13 15:12:36 2014 -0500

    Merge branch '4.2'

 Query/Builder.php | 46 ++++++++++++++++++++++++++++++++++++++++++++--
 composer.json     | 23 +++++++++++------------
 2 files changed, 55 insertions(+), 14 deletions(-)
 
commit 74b8089b6ff5aed2129cbd7e7559144b68d3f660
Author: Taylor Otwell &lt;taylorotwell@gmail.com&gt;
Date:   Fri Jun 13 14:28:23 2014 -0500

    Working on documentation block.

 Schema/Blueprint.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

</code></pre>
<p>如果不想攤在根目錄，而是放在 src 目錄的話，要再多做一步 <code>filter-branch</code>，--tree-filter 後面接的是 command 會被 shell 執行 (透過 <code>eval</code>)，所以這邊我們建立 src/ 目錄 （replay 當下可能不存在)，然後判斷根目錄的每個檔案如果不是一開始建立的 src/ 目錄，就把它搬到 src/ 裡去</p>
<pre><code>framework (database) $ git filter-branch -f --tree-filter 'mkdir -p src; for d in *; do if [ $d != &quot;src&quot; ]; then mv $d src/; fi; done;' database
Rewrite 291c2e9139086aefefb3db32e56675c67d92c1ec (1059/1059)
Ref 'refs/heads/database' was rewritten
</code></pre>
<p>結果就變這樣</p>
<pre><code>framework (database) $ tree -d
.
└── src
    ├── Capsule
    ├── Connectors
    ├── Console
    │   └── Migrations
    ├── Eloquent
    │   └── Relations
    ├── Migrations
    │   └── stubs
    ├── Query
    │   ├── Grammars
    │   └── Processors
    └── Schema
        └── Grammars

14 directories
</code></pre>
<p>最後，因為歷史被改寫的關係，<code>master</code> 與 <code>database</code> branch 除了 commit 訊息相似之外，已經是兩條平行的 tree 了，只要簡單 init 一個 git repository，並把創造出來的 <code>database</code> branch fetch 回來，然後 merge 就結束了 (等同於 git pull 的操作)。</p>
<pre><code>$ cd /path/to/new_repo
new_repo $ git init
Initialized empty Git repository in /path/to/new_repo/.git/
new_repo (master) $ git fetch /path/to/framework database
remote: Counting objects: 5889, done.
remote: Compressing objects: 100% (1821/1821), done.
remote: Total 5889 (delta 3026), reused 3762 (delta 3007)
Receiving objects: 100% (5889/5889), 1.54 MiB | 0 bytes/s, done.
Resolving deltas: 100% (3026/3026), done.
From ../framework
 * branch            database   -&gt; FETCH_HEAD
 
new_repo (master) $ git merge FETCH_HEAD
</code></pre>
<p>想像一下，補上 <code>composer.json</code> 和 autoload PSR-4 後，是不是就像是一個獨立的 composer package，建議可以用這個方式拆 module，module 一多就可以利用 composer 的 replace 來組 full stack 的 package，有點像是 Debian dummy package 或 FreeBSD meta port 的概念，拆細的彈性就在這裡。</p>
<hr>
<p>另外補充一下其他作法，--subdirectory-filter 那邊可以用 <code>git subtree</code>，上面兩段 <code>filter-branch</code> 可以用下面一行來完成...</p>
<pre><code>framework (database) $ git filter-branch --subdirectory-filter src/Illuminate/Database/ --index-filter 'git ls-files -s | sed &quot;s|\t\&quot;*|&amp;src/|&quot; | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &amp;&amp; mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' database
</code></pre>
<p>指令有點長，不過這一段有寫在 manpage 的範例裡，所以不用刻意去背它。</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[PHP 的 global 變數]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>自已曾經碰過，今天被問到的問題，覺得解釋的不太好，所以事後把它寫下來。</p>
<p>問題是這樣子：</p>
<p>某個 class 裡面的 method 呼叫一段在其他檔案定義的 function，回傳值卻不如預期。</p>
<p>實際把問題簡化成 sample code 如下：</p>
<p>假設 <code>app.php</code> 與 <code>lib.php</code> 都在同一層目錄</p>
<p>lib.php</p>
<pre><code class="language-language-php">&lt;?php

$exchange_rates = array(
    'TW' =&gt; 30,
);

function currency_convert($currency, $to)
{
    global $exchange_rates;
    return $currency * $exchange_rates[$to];
}
</code></pre>
<p>app.php</p>
<pre><code class="language-language-php">&lt;?php</code></pre>]]></description><link>https://blog.gasol.tw/global-variable-in-php/</link><guid isPermaLink="false">5e5f2a334e32c05f948a17e2</guid><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Mon, 09 Jun 2014 09:27:02 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>自已曾經碰過，今天被問到的問題，覺得解釋的不太好，所以事後把它寫下來。</p>
<p>問題是這樣子：</p>
<p>某個 class 裡面的 method 呼叫一段在其他檔案定義的 function，回傳值卻不如預期。</p>
<p>實際把問題簡化成 sample code 如下：</p>
<p>假設 <code>app.php</code> 與 <code>lib.php</code> 都在同一層目錄</p>
<p>lib.php</p>
<pre><code class="language-language-php">&lt;?php

$exchange_rates = array(
    'TW' =&gt; 30,
);

function currency_convert($currency, $to)
{
    global $exchange_rates;
    return $currency * $exchange_rates[$to];
}
</code></pre>
<p>app.php</p>
<pre><code class="language-language-php">&lt;?php

require __DIR__ . '/lib.php';

echo currency_convert(10, 'TW') . PHP_EOL;
</code></pre>
<p>這邊為了簡單呈現問題，不做 sanity check，上面這一段輸出預期會是 <code>300</code>，但是在特地的條件下 <code>currency_convert()</code> 的回傳的會是讓你驚訝的 <code>0</code>，我們把 <code>app.php</code> 修改為下面的內容：</p>
<pre><code class="language-language-php">&lt;?php

call_user_func(function() {
    require __DIR__ . '/lib.php';
});

echo currency_convert(10) . PHP_EOL;
</code></pre>
<p>這邊簡單用 anonymous function 來 require <code>lib.php</code> 來重製問題，故意讓 <code>currency_convert()</code> 壞掉，結果輸出就會是 <code>0</code>，知道問題了嗎？</p>
<p>與 C 一樣，PHP 在 function 裡的變數都是區域變數，唯一不同的地方在全域變數在 PHP function 裡必需要用 <code>global</code> 宣告才能看的到。</p>
<p>所以在上面的例子中， $exchange_rates 因為 anonymous function 的關係，他從全域變數變成了區域變域，只在 anonymous function 裡才存取的到。</p>
<p>千萬別認為神經病才用 anonymous function 來 require library！市面上所見到 autoload 機制的 class loader <a href="https://github.com/composer/composer/blob/master/src/Composer/Autoload/ClassLoader.php#L375">1</a>，大多都是在 method (或 function) 裡來 require 檔案，然後就引爆地雷了。</p>
<p>workaround 的方法就是在看的到變數的地方做手腳，所以有幾種方法：</p>
<ol>
<li>在 require 前宣告 global $var</li>
<li>用 superglobal 變數 $GLOBALS</li>
<li>你有權限 touch 的到 <code>lib.php</code> 的話，在外面的 $exchange_rate 也可以宣告成 global，global 並沒有限制一定要在 function 裡使用，在 function 外也可以使用，因為檔案在 function 裡被引用的話，此時的 <code>global</code> 就發揮用處了</li>
</ol>
<hr>
<p>後話</p>
<p>有一定年紀的 PHP Application，如果持續維護至今的話，應該都會面臨 programming paradigm 從 precedural 轉換至 object-oriented 的過程，在轉換過程之中就很容易遇到這類的問題。</p>
<p>這個問題是義大利麵與定食共享的結果。</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Welcome to Ghost]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>You're live! Nice. We've put together a little post to introduce you to the Ghost editor and get you started. You can manage your content by signing in to the admin area at <code>&lt;your blog URL&gt;/ghost/</code>. When you arrive, you can select this post from a list</p>]]></description><link>https://blog.gasol.tw/welcome-to-ghost/</link><guid isPermaLink="false">5e5f2a334e32c05f948a17e1</guid><category><![CDATA[Getting Started]]></category><dc:creator><![CDATA[Gasol Wu]]></dc:creator><pubDate>Thu, 20 Mar 2014 05:36:16 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>You're live! Nice. We've put together a little post to introduce you to the Ghost editor and get you started. You can manage your content by signing in to the admin area at <code>&lt;your blog URL&gt;/ghost/</code>. When you arrive, you can select this post from a list on the left and see a preview of it on the right. Click the little pencil icon at the top of the preview to edit this post and read the next section!</p>
<h2 id="gettingstarted">Getting Started</h2>
<p>Ghost uses something called Markdown for writing. Essentially, it's a shorthand way to manage your post formatting as you write!</p>
<p>Writing in Markdown is really easy. In the left hand panel of Ghost, you simply write as you normally would. Where appropriate, you can use <em>shortcuts</em> to <strong>style</strong> your content. For example, a list:</p>
<ul>
<li>Item number one</li>
<li>Item number two
<ul>
<li>A nested item</li>
</ul>
</li>
<li>A final item</li>
</ul>
<p>or with numbers!</p>
<ol>
<li>Remember to buy some milk</li>
<li>Drink the milk</li>
<li>Tweet that I remembered to buy the milk, and drank it</li>
</ol>
<h3 id="links">Links</h3>
<p>Want to link to a source? No problem. If you paste in url, like <a href="http://ghost.org">http://ghost.org</a> - it'll automatically be linked up. But if you want to customise your anchor text, you can do that too! Here's a link to <a href="http://ghost.org">the Ghost website</a>. Neat.</p>
<h3 id="whataboutimages">What about Images?</h3>
<p>Images work too! Already know the URL of the image you want to include in your article? Simply paste it in like this to make it show up:</p>
<p><img src="https://ghost.org/images/ghost.png" alt="The Ghost Logo"></p>
<p>Not sure which image you want to use yet? That's ok too. Leave yourself a descriptive placeholder and keep writing. Come back later and drag and drop the image in to upload:</p>
<p>![A bowl of bananas]</p>
<h3 id="quoting">Quoting</h3>
<p>Sometimes a link isn't enough, you want to quote someone on what they've said. It was probably very wisdomous. Is wisdomous a word? Find out in a future release when we introduce spellcheck! For now - it's definitely a word.</p>
<blockquote>
<p>Wisdomous - it's definitely a word.</p>
</blockquote>
<h3 id="workingwithcode">Working with Code</h3>
<p>Got a streak of geek? We've got you covered there, too. You can write inline <code>&lt;code&gt;</code> blocks really easily with back ticks. Want to show off something more comprehensive? 4 spaces of indentation gets you there.</p>
<pre><code>.awesome-thing {
    display: block;
    width: 100%;
}
</code></pre>
<h3 id="readyforabreak">Ready for a Break?</h3>
<p>Throw 3 or more dashes down on any new line and you've got yourself a fancy new divider. Aw yeah.</p>
<hr>
<h3 id="advancedusage">Advanced Usage</h3>
<p>There's one fantastic secret about Markdown. If you want, you can  write plain old HTML and it'll still work! Very flexible.</p>
<input type="text" placeholder="I'm an input field!">
<p>That should be enough to get you started. Have fun - and let us know what you think :)</p>
<pre><code class="language-language-php">&lt;?php

echo &quot;Hello World\n&quot;;
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>