PHP 7.0 下安裝 phpLDAPadmin 發生錯誤的修正方法

在稍具規模的網路環境中, 網管時常選用 LDAP 來進行帳號的統整管理, 一方面提供管理便利度, 另一方面使用者也不必因為不同系統而記憶不同帳號, phpLDAPadmin 是一套常見的 LDAP 管理介面, 但 phpLDAPadmin 已經很久沒有更新了, 若想要安裝在 PHP 5.6 以上, 甚至是最新版的 PHP 7 的環境中, 是無法順利安裝的, 因此我們需要做些修正.

Session 相關錯誤

打開 index.php 時出現下列錯誤:

  • Undefined variable: _SESSION in …. on line 379
  • Fatal error: Uncaught Error: Call to a member function getValue() on null in … on line 379

導致這個錯誤的主要原因是因為 phpLDAPadmin 需要使用 session, 在 PHP 中若要使用 session, 需要先執行 session_start() 這個函數, 但不知道為什麼 phpLDAPadmin 並沒有這麼做, 因此要解決這個問題, 一種方法是自己找正確的地方加上 session_start(), 另一種方法則是啟動 auto_start 這個特性, 這邊介紹如何啟動 auto_start 這個特性

編輯 php.ini 中的 session.auto_start

session.auto_start = 0

修改為

session.auto_start = 1

password_hash 相關錯誤

修正完 session 的問題後, 畫面上還有一個關於 password_hash 的錯誤

  • Fatal error: Cannot redeclare password_hash() in … on line 2236

造成這個錯誤的原因在於 php 5.5 以後內建了 password_hash 這個函數, 導致內建的 password_hash 與 phpLDAPadmin 自己的 password_hash 發生衝突, 因此只要將 phpLDAPadmin 的 password_hash 名稱替換成別的名稱就可以避開這個問題了

具體的解決辦法如下

對檔案打 patch

wget https://raw.githubusercontent.com/osixia/docker-phpLDAPadmin/stable/image/service/phpldapadmin/assets/php5.5.patch
patch -p1 -d ./phpldapadmin < php5.5.patch

修改 TemplateRender.php

sed -i "s/password_hash/password_hash_custom/g" ./phpldapadmin/lib/TemplateRender.php

Mac 的使用者會遇到 invalid command code W 這個錯誤, 具體原因與解決辦法請參考 MAC 下執行 sed 指令出現錯誤: invalid command code W

記得把指令中的 ./phpldapadmin 替換成 phpldapadmin 的實際位置

MAC 下執行 sed 指令出現錯誤: invalid command code W

sed 是 Linux/Mac 下非常好用的文件內容替換工具, sed -i "s/old/new/g" /path/to/file 可以讓我們輕鬆的將 /path/to/file 中的 old 都替換成 new, 但最近發現 sed 在 mac 與 Linux 上的行為有些許不同, 本來在 Linux 上能夠正常運作的指令在 MAC 上卻無法運作, sed 執行時出現錯誤 invalid command code W

MAC 上的 sed 手冊對於 -i 的描述:

-i extension
    Edit files in-place, saving backups with the specified extension.  If a zero-length extension is given, no backup will be saved.  It is not recommended to give a zero-
    length extension when in-place editing files, as you risk corruption or partial content in situations where disk space is exhausted, etc.

而 Linux 上的 sed 手冊對於 -i 的描述:

-i[SUFFIX], --in-place[=SUFFIX]
    edit files in place (makes backup if SUFFIX supplied)

根據手冊的中的描述, sed 在帶有 -i 參數時, 會針對處理的檔案進行備份, 而 -i 參數接受一個字串作為備份檔案的後綴( ex: test.php => test_ext.php ), 這個後綴在 Linux 下是可選, Mac 下是必要的, 雖然 Mac 下的 sed 可以給空字串來忽略這個選項, 但備份很重要, 備份一下啦!!

在知道差異後, 我們只要將後綴加上去就可以解決這個問題了!

Linux:
sed -i "s/old/new/g" /path/to/file

Mac:
sed -i "" "s/old/new/g" /path/to/file

基於 Echo Shorthand Tags 的 PHP Webshell

駭客在入侵網站後, 通常都會放入網頁後門程式(Webshell), 用以確保控制權不容易因為弱點被修復而消失.

下方是常見的簡短 Webshell (一句話木馬)

<?php system($_GET[0]);

最近發現了另一種簡短的 Webshell, 利用了 PHP shorthand tag, 寫法如下

<?=`ls`;

其效果等價於

<?php echo system('ls');

很多管理員認為預設不開啟 short tags 就可以避開這種後門, 但事實並非如此

在 php.ini 中掌管 short tag 的參數為

short_open_tag = Off

官方對於 short_open_tag 的說明中有提到 <?= 並不包含其中, 因此在做後門探測的時候, 記得也要針對這類型的後門進行檢查

偉哉PHP

Laravel 常見問題: Specified key was too long

Laravel 在 5.4 版之後為了支援 emoji , 因此將資料編碼改為 utf8mb4. 由於 utf8mb4 的儲存空間需求膨脹了4倍, 導致預設長度無法正常寫入資料庫.

這個問題會在 MySQL 5.7.6 以下與 MariaDB 的環境中出現以下錯誤

[PDOException]
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes

在 Migrate 過程中出現這個問題的解決方法有兩種

  1. 更新到 MySQL 5.7.7 以上版本
  2. 調整 Laravel 的預設資料長度

升級 MySQL 的方法就不細說了, 接下來主要說明調整 Laravel 預設值的方法

首先, 編輯專案目錄下的 app/Providers/AppServiceProvider.php

新增一個引用

use Illuminate\Support\Facades\Schema;

接著修改 boot function, 將預設長度定為 191

public function boot()
{
    Schema::defaultStringLength(191);
}

重新執行 php artisan migrate 就不會有問題了

最後附上完整的 AppServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Schema::defaultStringLength(191);
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

Linux 設定修改時區

Linux 上時間分成兩個部分, Universal Time 與 Local Time, Universal Time 一直會維持為 UTC 時間, 而 Local Time 則是使用者所在的時區時間, 通常我們在安裝 Linux 的時候就會完成時區的設定, 但是在 Cloud 上預設都維持在 UTC +0 時區, 當我們機房跨足不同時區時可能會有同步的問題, 因此正確的設定時區是非常重要的.

檢查時區

當主機還沒有設定時區時, 輸入指令 date 系統返回的時間會是 UTC, 若主機已經有設定時區 UTC 會變成其他時區

$ date
Sat Jun 17 15:28:00 UTC 2017

設定時區

我們可以透過以下指令進行時區設定

sudo dpkg-reconfigure tzdata

系統會跳出視窗要求你選擇所在的地理區

接著選擇時區

當設定完成後, 系統會顯示目前的設定

Current default time zone: 'Asia/Taipei'
Local time is now:      Sat Jun 17 23:51:12 CST 2017.
Universal Time is now:  Sat Jun 17 15:51:12 UTC 2017.

指令 date 所顯示的時間變成帶有時區的時間了!

Sat Jun 17 23:51:55 CST 2017

這樣一來就完成時區的設定了, 若要修改時區只要把上面的步驟在做一次就可以了.

Electron – Cannot find module ‘app’

最近將舊版的 electron 升級到最新版, 結果所有程式都不能啟動了, 全部回傳 Error: Cannot find module 'app', 在翻閱新版手冊發找到了解決辦法

問題

升級到新版 Electron 出現 Error: Cannot find module 'app'

成因

Electron 新版將 app 模組整合到 electron 內

解決辦法

舊版手冊的範例寫法如下

var app = require('app');
var BrowserWindow = require('browser-window'); 

由於 app 被整合到 electron 內了, 因此只要改寫成下方的寫法就可以正常運作了

var electron = require('electron');
var app = electron.app;
var BrowserWindow = electron.BrowserWindow;

Electron – Uncaught ReferenceError: $ is not defined

最近因為需要將新的 Web App 封裝成應用程式, 團隊決定使用 Github Electron 進行封裝, 但封裝後的程式無法運作, 本來以為是 VUE 造成的, 直到在 Console 看到了 Uncaught ReferenceError: $ is not defined 才確定是 jQuery 造成的, 但說也奇怪, VUE 正常而 jQuery 卻沒辦法註冊, 粗略看起來應該不是 js 沒有載入的問題, 經過幾番研究後確定了問題成因, 這邊做一個小筆記紀錄一下.

問題

Electron 出現 Uncaught ReferenceError: $ is not defined 錯誤

成因

Electron 的 Render 使用了 Nodejs, 並且掛入 require 到全域變數, 導致 jQuery 註冊的 $ 被覆蓋了

解決辦法

jQuery 實際上是有正確被載入的, 因此我們只需要透過下面的方法重新註冊 $ 就可以了

try {$ = jQuery = module.exports;} catch(e) {}

如此一來, jQuery 重新綁定回 $, 系統就可以正常運作了

Apache .htaccess 沒有效果

我們知道在 Apache 中可以使用 .htaccess 來針對當下目錄做相關設定, 但時常發生 .htaccess 無效的狀況, 這邊分享一下一些常見的原因與排除方法

沒有複寫權限

Apache 根目錄預設是禁止複寫設定的, 可以透過編輯 httpd.conf 當中的設定開啟複寫功能

編輯 httpd.conf

sudo vim /etc/apache/httpd.conf

找到相關目錄設定(這邊以內建路徑為例)

<Directory "/Library/WebServer/Documents">
    Options FollowSymLinks Multiviews
    MultiviewsMatch Any

        AllowOverride None

        Require all granted
</Directory>

將 AllowOverride 修改為 All

<Directory "/Library/WebServer/Documents">
    Options FollowSymLinks Multiviews
    MultiviewsMatch Any

        AllowOverride All

        Require all granted
</Directory>

重新啟動 Apache 即可

檔案名稱錯誤

Apache 可以修改 .htaccess 的名稱, 可能因為某些原因名稱被修改了
可以尋找設定檔中的參數 AccessFileName 來確認正確名稱

模組無效

很多時候其實 .htaccess 有生效, 只是是因為 .htaccess 中有進行模組是否存在的相關檢查, 以至於我們以為沒有生效

.htaccess 可能有類似的內容

<IfModule mod_rewrite.c>
    .......
</IfModule>

上述的 .htaccess 會檢查 Apache 是否有 rewrite 這個模組, 若有才執行, 這種狀況變成要啟動該模組才會正常運行

編輯 httpd.conf, 找到下面的內容並將前方的 # 移除

#LoadModule rewrite_module libexec/apache2/mod_rewrite.so

將 MAC 內建的 PHP 5.6 升級到 PHP 7

在 MAC 上進行開發時使用的是 MAC 內建 PHP 5.6, 但 PHP 7 實在是很好用, 決定把 MAC 的 PHP 5.6 升級成 PHP 7, Mac 上要升級到 PHP 7非常簡單, 國外已經有人提供編譯好的版本與安裝腳本, 使用起來非常容易

相關資料

官方網站 php-macosx

安裝 PHP 7

選擇一個想要安裝的 PHP 版本, 目前支援 PHP 7.0 與 PHP 7.1

選擇 PHP 7.0

curl -s http://php-osx.liip.ch/install.sh | bash -s 7.0

選擇 PHP 7.1

curl -s http://php-osx.liip.ch/install.sh | bash -s 7.1

測試結果

接著寫入一個 phpinfo

<?php phpinfo();

看到下面這個畫面就代表我們成功升級為 PHP 7 了!

調整 Apache 設定

由於 MAC 預設使用 PHP 5, MAC 內建的 Apache 的 PHP 解釋器預設是連結到 libphp5.so, 當安裝新的 PHP 7 之後會產生衝突, 因此我們必須關閉舊版的模組, Apache 才能正常運作

關閉 PHP 5 模組

編輯 httpd.conf

sudo vim /etc/apache2/httpd.conf

找到原先的 libphp5.so 模組

LoadModule php5_module libexec/apache2/libphp5.so

在前面加上 # 將其註解

#LoadModule php5_module libexec/apache2/libphp5.so

重新啟動 Apache

sudo apachectl restart

Laravel 電話長度與選填欄位的實作筆記

最近在處理計畫的網站, 計畫的網站當中有一個選填電話的欄位, 在使用 Laravel 內建 Validation 進行長度檢查時意外踩到坑, 仔細查閱手冊後才發現 Max, Min 的行為與心中所有有所差距, 因此做個筆記避免往後夥伴踩到一樣的坑

Laravel 的 Validation 非常強大, 若要讓欄位選填並且只有在有填寫內容時才進行其他驗證, 可以使用內建的 nullable 達到效果, 而不需要使用 required

$rules = [ 'phone'=>'nullable' ];

針對數字的部分可以用 numeric 來達到驗證

$rules = [ 'phone'=>'nullable|numeric' ];

在長度檢查的部分我一開始是用了 min 與 max 來檢查, 結果無論如何都會觸發長度過長的錯誤, 仔細看過手冊後發現 Laravel 會去猜輸入的型別, 如果猜測結果是數字則 min, max 會變成比大小(min < x < max) 而不是預期的長度檢查, 正確長度檢查要使用 digits_between 這個規則才對

$rules = [ 'phone'=>'nullable|numeric|digits_between:10,12' ];