2015年12月28日 星期一

遠振主機 escapeshellarg() has been disabled for security reasons

用遠振主機又踩雷啦,遠振將escapeshellarg這個函式disabled 所以只要有用到 SebastianBergmann\Environment\Runtime Symfony\Component\Console\Input\Input Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser Symfony\Component\Process\ProcessUtils Tracy\Debugger 都會提示 escapeshellarg() has been disabled for security reasons 不過還好php5.3之後就支援namespace,可以讓我們輕鬆略過這個問題 (如果使用的套件不支援namespace那就只好認命的去修改程式囉) 只要require下面的檔案就可以解決這些問題了

namespace Yuan\Jhen
{
    if (function_exists('escapeshellarg') === true) {
        function escapeshellarg($input)
        {
            return \escapeshellarg($input);
        }
    } else {
        function escapeshellarg($input)
        {
            $input = str_replace('\'', '\\\'', $input);

            return '\''.$input.'\'';
        }
    }
}

namespace SebastianBergmann\Environment
{
    function escapeshellarg($input)
    {
        return \Yuan\Jhen\escapeshellarg($input);
    }
}

namespace Symfony\Component\Console\Input
{
    function escapeshellarg($input)
    {
        return \Yuan\Jhen\escapeshellarg($input);
    }
}

namespace Symfony\Component\HttpFoundation\File\MimeType
{
    function escapeshellarg($input)
    {
        return \Yuan\Jhen\escapeshellarg($input);
    }
}

namespace Symfony\Component\Process
{
    function escapeshellarg($input)
    {
        return \Yuan\Jhen\escapeshellarg($input);
    }
}

namespace Tracy
{
    function escapeshellarg($input)
    {
        return \Yuan\Jhen\escapeshellarg($input);
    }
}

2015年12月22日 星期二

利用Git來升級Laravel

照著影片操作,就會出現conflicts,處理完conflicts之後,再執行

composer install

這樣大致上就升級完畢了,而且如果升級出問題還可以利用git本身的功能來進行還原...


2015年12月20日 星期日

php 偵測語系

function getDefaultLanguage()
{
    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
        return parseDefaultLanguage($_SERVER['HTTP_ACCEPT_LANGUAGE']);
    } else {
        return parseDefaultLanguage(null);
    }
}

function parseDefaultLanguage($http_accept, $deflang = 'zh-TW')
{
    if (isset($http_accept) && strlen($http_accept) > 1) {
        # Split possible languages into array
        $x = explode(',', $http_accept);
        foreach ($x as $val) {
            #check for q-value and create associative array. No q-value means 1 by rule
            if (preg_match("/(.*);q=([0-1]{0,1}.\d{0,4})/i", $val, $matches)) {
                $lang[$matches[1]] = (float) $matches[2];
            } else {
                $lang[$val] = 1.0;
            }
        }

        #return default language (highest q-value)
        $qval = 0.0;
        foreach ($lang as $key => $value) {
            if ($value > $qval) {
                $qval = (float) $value;
                $deflang = $key;
            }
        }
    }

    return strtolower($deflang);
}

echo getDefaultLanguage();

資料庫備份工具

class DatabaseClone
{
    public $link;

    public function __construct($host, $username, $password)
    {
        $this->link = mysqli_connect($host, $username, $password);
        mysqli_query($this->link, 'SET NAMES utf8');
        mysqli_query($this->link, 'SET AUTOCOMMIT = 0');
        mysqli_query($this->link, 'SET UNIQUE_CHECKS = 0');
        mysqli_query($this->link, 'SET FOREIGN_KEY_CHECKS = 0');
    }

    public function fetch($query)
    {
        $results = [];
        $query = mysqli_query($this->link, $query);
        while ($row = mysqli_fetch_assoc($query)) {
            $results[] = $row;
        }

        return $results;
    }

    public function copy($copyTo)
    {
        set_time_limit(-1);
        ini_set('memory_limit', -1);
        $query = mysqli_query($this->link, $sql = 'show databases');
        while ($database = mysqli_fetch_assoc($query)) {
            $database = $database['Database'];
            if (in_array($database, ['mysql', 'information_schema', 'performance_schema'], true) === true) {
                continue;
            }

            $createDatabase = mysqli_fetch_assoc(mysqli_query($this->link, 'SHOW CREATE DATABASE `'.$database.'`'));
            mysqli_query($copyTo->link, $sql = 'DROP DATABASE IF EXISTS `'.$database.'`') or var_dump($sql);
            mysqli_query($copyTo->link, $sql = $createDatabase['Create Database']) or var_dump($sql);

            mysqli_select_db($this->link, $database);
            mysqli_select_db($copyTo->link, $database);

            $query2 = mysqli_query($this->link, $sql = 'show tables');
            while ($table = mysqli_fetch_assoc($query2)) {
                $table = current($table);
                $createTable = mysqli_fetch_assoc(mysqli_query($this->link, 'SHOW CREATE TABLE `'.$table.'`'));
                mysqli_query($copyTo->link, $sql = $createTable['Create Table']) or var_dump($sql);
                $counts = current(mysqli_fetch_assoc(mysqli_query($this->link, 'SELECT COUNT(*) as counts FROM `'.$table.'`')));
                $range = range(0, $counts - 1);
                $offset = 0;
                $limit = 200;
                $chunks = array_chunk($range, $limit);
                foreach ($chunks as $chunk) {
                    $query3 = mysqli_query($this->link, $sql = 'SELECT * FROM `'.$table.'` LIMIT '.$limit.' OFFSET '.$offset) or var_dump($database, $sql);
                    while ($row = mysqli_fetch_assoc($query3)) {
                        $keys = [];
                        $values = [];
                        foreach ($row as $key => $value) {
                            $keys[] = '`'.$key.'`';
                            $values[] = "'".addslashes($value)."'";
                        }
                        mysqli_query($copyTo->link, $sql = 'INSERT INTO `'.$table.'` ('.implode(',', $keys).') VALUES ('.implode(',', $values).')') or var_dump($database, $sql);
                    }
                    $offset += count($chunk);
                }
            }
        }
    }
}

$database = new DatabaseClone('localhost:3306', 'root', '');
$database->copy(new DatabaseClone('localhost:3308', 'root', ''));

2015年11月7日 星期六

使用instance來進行Tracy Panel

// PHP程式碼
include __DIR__.'/vendor/autoload.php';

use Tracy\Debugger;
use Tracy\IBarPanel;

class CustomPanel implements IBarPanel
{
    /**
     * Renders HTML code for custom tab.
     * @return string
     */
    public function getTab()
    {
        return 'sql';
    }

    /**
     * Renders HTML code for custom panel.
     * @return string
     */
    public function getPanel()
    {
        $data = $this->data;
        ob_start();
        require __DIR__.'/custompanel.php';

        return ob_get_clean();
    }

    public function push($data)
    {
        $this->data[] = $data;
    }

    public $data = [];

    private static $_instance;

    public static function instance()
    {
        if (static::$_instance === null) {
            static::$_instance = new static;
        }

        return static::$_instance;
    }
}

// 開發環境
$environment = Debugger::DEVELOPMENT;
// 啟用
Debugger::enable($environment);

Debugger::getBar()
    ->addPanel(CustomPanel::instance());

$i = 0;
CustomPanel::instance()->push([
    'sql' => 'select * from user where id = '.++$i,
]);

CustomPanel::instance()->push([
    'sql' => 'select * from user where id = '.++$i,
]);

CustomPanel::instance()->push([
    'sql' => 'select * from user where id = '.++$i,
]);
//樣板<h1>SQL</h1>

<div class="tracy-inner">
    <table>
        <?php foreach ($data as $key => $value): ?>
            <tr><td><?php echo $value['sql']; ?></td></tr>
        <?php endforeach ?>
    </table>
</div>

2015年10月23日 星期五

Laravel的Session不等於PHP Native Session

在Laravel內我們可能寫了以下的程式碼

get('/session', function () {
    session_start();
    session()->put('foo', 'bar');
    $_SESSION['foo'] = 'bar';

    header('location: '.url('/session/display'));
    exit;
});

get('/session/display', function () {
    session_start();
    ob_start();
    dump(session()->get('foo'));
    dump($_SESSION['foo']);
    $content = ob_get_clean();

    // output null, 'bar'

    return $content;
});

會發現輸出結果完全不如我們所預期,為什麼會這樣呢?我們就好好的來做個探討吧...

原因出在Laravel的Session根本不是用PHP Native Session

在遇到這個狀況後,於是就去查了一下Laravel Session倒底是怎麼一回事,發現Laravel的程式碼內根本沒有執行session_start()啊

Laravel Session倒底是怎麼運作的

追了一下Laravel的原始碼,發現它是利用Illuminate\Session\Middleware\StartSession來進行php session模擬的, 在Laravel執行的最後階段在將存在記憶體內的資料進行實體寫入, 所以只要在我們只要在程式內任意一個地方執行了php的exit, die, header('location: xxx'); 那Session就不會被存入檔案內,自然在下一次連線也就取不到值了

有沒有解決方案

有,當然有,只要輸入

get('/session', function () {
    session_start();
    session()->put('foo', 'bar');
    $_SESSION['foo'] = 'bar';
    session()->save(); // 手動寫入
    dump($_SESSION['foo']);
    $content = ob_get_clean();

    // output 'bar', 'bar'

    return $content;
});

後記

為什麼會有這一篇的出現,因為我們可能會用很多其它已經寫好的library,但它們就是有用到session_start();外加header, die, exit等等指令啊,所以記錄一下來提醒自己囉

2015年9月20日 星期日

重寫Laravel Socialite

最近需要用到OAuth的Client套件 所以就使用目前最多人使用的Laravel 不過需要的部份只有Laravel Socialite

看了一下composer.json的相依性

    "require": {
        "php": ">=5.4.0",
        "illuminate/contracts": "~5.0",
        "illuminate/http": "~5.0",
        "illuminate/support": "~5.0",
        "guzzlehttp/guzzle": "~5.0|~6.0",
        "league/oauth1-client": "~1.0"
    },

只需要這些package並不需要整個Laravel 所以就開始使用它 不過在使用的過程遇到了不少問題 它所需要的package其實不止這些 所以程式在撰寫的過程中確實遇到不少問題 再在上在本機端上開發會遇到cURL error 60: SSL certificate problem 於是決定以PHPoAuthLib進行開發 所以就寫了Recca0120 Socialite 並使它可以獨立使用

Demo

目前只先實作Laravel Socialite原本的功能,之後會視情形再加功能

OAuth1 BitBucket Twitter OAuth2 Facebook GitHub Google Instagram LinkedIn

2015年9月19日 星期六

cURL error 60: SSL certificate problem: unable to get local issuer certificate

Alt text

在使用Guzzle時,遇到cURL error 60: SSL certificate problem: unable to get local issuer certificate 只要下載ca-bundle.crt並放到相對應的路徑

[
    // Red Hat, CentOS, Fedora (provided by the ca-certificates package)
    '/etc/pki/tls/certs/ca-bundle.crt',
    // Ubuntu, Debian (provided by the ca-certificates package)
    '/etc/ssl/certs/ca-certificates.crt',
    // FreeBSD (provided by the ca_root_nss package)
    '/usr/local/share/certs/ca-root-nss.crt',
    // OS X provided by homebrew (using the default path)
    '/usr/local/etc/openssl/cert.pem',
    // Google app engine
    '/etc/ca-certificates.crt',
    // Windows?
    'C:\\windows\\system32\\curl-ca-bundle.crt',
    'C:\\windows\\curl-ca-bundle.crt',
];

如果再無法正常運作的話就得去修改php.ini 再加入

[curl]
curl.cainfo=C:\Windows\curl-ca-bundle.crt

[openssl]
openssl.cafile=C:\Windows\curl-ca-bundle.crt

就可以正常運作了

2015年9月14日 星期一

發撲克牌

在奇摩知識+看到有人發問發撲克牌,依花色及數字大小做排序,所以我就寫了這個程式囉

function Poker($member = 4, $POSTCARDS = 52)
{
    /*撲克花色*/
    $Poker = [
        'Spades',
        'Hearts',
        'Diamonds',
        'Clubs',
    ];
    /*人頭牌*/
    $CARD = [
        11 => 'J',
        12 => 'Q',
        13 => 'K',
    ];

    /*發牌順序*/
    $P = range(0, $POSTCARDS - 1);
    shuffle($P);

    /*存放結果陣列*/
    $result = [];

    $total = count($P);
    for ($i = 0; $i < $total; $i++) {
        /*發給玩家*/
        $t = $i % $member + 1;
        /*發牌編號 0-51*/
        $v = $P[$i];
        /*發牌花色 Spades,Hearts,Diamonds,Clubs*/
        $c = $Poker[$v % count($Poker)];
        /*花色大小 1-13*/
        $k = ($v % 13) + 1;
        $result[$t][$c][$k] = (in_array($k, array_keys($CARD))) ? $CARD[$k] : $k;
        /*依牌大小排序*/
        krsort($result[$t][$c]);
        /*依花色排序*/
        krsort($result[$t]);
    }

    return $result;
}
/*印出結果*/
print_r(Poker(1, 13));
/*如果要發4個人52張牌*/
print_r(Poker(4, 52));

php 下載限速

function getlocalfile($filename, $readmod = 1, $range = 0)
{
    if ($fp = @fopen($filename, 'rb')) {
        @fseek($fp, $range);
        $download_rate = 10;//限制网速10kb/s
        while (! feof($fp)) {
            print fread($fp, round($download_rate * 1024));
            flush();
            ob_flush();
            sleep(1);
        }
    }
    @fclose($fp);
    @flush();
    @ob_flush();
}

2015年8月26日 星期三

VirtualBox 5.0.2安裝Mac OS X 10.10 (Yosemite)

新增虛擬主機,並依照圖片中的設定即可正常安裝

  1. 版本請選擇 Mac OS X 10.10 Yosemite (64-bit) Alt text

2. 晶片組 ICH9 指標裝置 USB平板 延伸功能 全選 Alt text

3 啟用 VT-x/AMD-V 啟用Nested Paging Alt text

4 視訊記憶體調到128M 啟用3D加速 Alt text

5 掛載Yosemite原版ISO Alt text

6. 執行以下指令

VBoxManage setextradata "虛擬機器名稱" "VBoxInternal/Devices/efi/0/Config/DmiSystemProduct" "MacBookPro11,3"
VBoxManage setextradata "虛擬機器名稱" "VBoxInternal/Devices/efi/0/Config/DmiSystemVersion" "1.0"
VBoxManage setextradata "虛擬機器名稱" "VBoxInternal/Devices/efi/0/Config/DmiBoardProduct" "Iloveapple"
VBoxManage setextradata "虛擬機器名稱" "VBoxInternal/Devices/smc/0/Config/DeviceKey" "ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc"
VBoxManage setextradata "虛擬機器名稱" "VBoxInternal/Devices/smc/0/Config/GetKeyFromRealSMC" 1
VBoxManage setextradata "虛擬機器名稱" "VBoxInternal2/EfiGopMode" 4

這樣就能在Virtualbox 5.0.2上正常安裝Yosemite

2015年8月14日 星期五

Laravel 5 取得 Artisan::call的執行結果

// 輸出結果
class WelcomeController extends controller {
    public function index() {
        return response()->stream(function() {
            Artisan::call('inspire');
            echo Artisan::output();
        });
    }
}

2015年8月9日 星期日

Laravel 5 資料庫timezone設定

原本在設定database的timezone的設定方式是採取

'mysql' => [
    'driver' => 'mysql',
    'host' => env('DB_HOST', 'localhost'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'charset' => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix' => '',
    'strict' => false,
    'options' => [
        PDO::MYSQL_ATTR_INIT_COMMAND => 'SET time_zone = "+08:00"'
    ],
],

後來卻在laravel的原始碼發現 Alt text 所以timezone的設定只要這樣寫就可以

'mysql' => [
    'driver' => 'mysql',
    'host' => env('DB_HOST', 'localhost'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'charset' => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix' => '',
    'strict' => false,
    'timezone' => '+08:00',
],

2015年8月2日 星期日

找出Windows的序號

重灌或升級windows時有時候會忘記序號 又找不到序號時就很煩好 還好找到一個免費又簡單的找出序號的軟體

Lazesoft Windows Key Finder

而且它是Open Source Freeware的軟體 不用擔心會有病毒喔

支援的作業系統

  • Windows 2000 SP4
  • Windows XP
  • Windows Vista
  • Windows 7
  • Windows 8
  • Windows 8.1
  • Windows Server 2003
  • Windows Server 2008
  • Windows Server 2008 R2
  • Windows Server 2012
  • Windows Server 2012 R2 支援的OFFICE
  • Microsoft Office 2000
  • Microsoft Office 2003
  • Microsoft Office 2007
  • Microsoft Office 2010
  • Microsoft Office 2013 Alt text

2015年7月27日 星期一

使用Laravel artisan schedule:run時 exec被禁用時的替代方案

namespace App\Console;

use Artisan;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        'App\Console\Commands\Inspire',
    ];

    /**
     * Define the application's command schedule.
     *
     * @param \Illuminate\Console\Scheduling\Schedule $schedule
     */
    protected function schedule(Schedule $schedule)
    {
        // 當exec被disabled時只要改成使用Artisan::call('command')即可
        $schedule->call(function () {
             Artisan::call('inspire');
        })
        ->hourly();
    }
}

遠振主機上利用crontab執行 laravel artisan schedule:run

在專案中有個需求需要在固定時間去抓取固定資料 既然都已經使用laravel所以當然開始使用artisan寫寫console指令 於是很快速的寫完程式,當然第一時間就在自己本機上測試 測試的結果當然是一切都正常 所以很快的就把程式上傳到遠振主機上 並且設定了Cron的排程

* * * * * php /path/to/artisan schedule:run 1>> /dev/null 2>&1

設定完後,想說一切都妥當了, 但程式一直出錯 系統就回應

[ErrorException]
Invalid argument supplied foreach()

這要怎麼debug啊 而且明明在自己電腦內測試都能正常執行啊

所以只好開始研究程式碼囉,查到最後原來.....

遠振主機的php預設為 php-cgi 所以無法取得正確的參數 如果要artisan正常執行的話必須使用php-cli 所以在遠振主機上要執行artisan應該要改為

* * * * * /usr/bin/php-cli /path/to/artisan schedule:run 1>> /dev/null 2>&1

應該所有的cpanel主機只要是可選php版本的都會遇到這個問題......

php-cli和php-cgi兩個在console上執行的差異 php-cli 的參數會存入 $_SERVER['argv']; php-cgi的參數則會存入 $_GET; 如果要讓php-cgi能正常執行則可以用以下做法

if (empty($_GET) === false) {
    foreach ($_GET as $key => $value) {
        $_SERVER['argv'][] = $key;
    }
}

2015年4月12日 星期日

sublime 安裝 less

  1. 安裝nodejs

  2. 開啟cmd,並執行

    npm install -j less
    
  3. sublime安裝 LESS, smart less build

  4. 修改smart less build設定檔

    {
     "source_map": false
     "custom_args": "--clean-css=\"--s1 --advanced --compatibility=ie8\""
    }
    

sublime 安裝 coffeescript

  1. 安裝nodejs

  2. 開啟cmd,並執行

    npm install -j coffee-script
    
  3. sublime安裝 Better CoffeeScript

  4. 修改coffeescript設定檔

    {
     "checkSyntaxOnSave": true
     "lintOnSave": true
     "lintConfFile": true
    }
    

sublime syncing

windows內如何同步sublime的設定檔至多台電腦呢? 我們可以利用Dropbox加mklink的方式來做設定喔

請使用系統管理者權限開啟cmd

1.將已設定好的sublime的設定檔移至dropbox內

mkdir %USERPROFILE%\Dropbox\Sublime
cd %APPDATA%\"Sublime Text 3\Packages"
copy /Y User\* %USERPROFILE%\Dropbox\Sublime

2.同步所有資料夾

cd %APPDATA%\"Sublime Text 3\Packages"
rmdir /S /Q User
mklink /D User %USERPROFILE%\Dropbox\Sublime

sublime terminail套件使用git bash

1.安裝 git

2.sublime安裝Terminal

3.將Terminal => Settings - Default的內容複製到Terminal => Settings - User

4.修改

{
    "terminal": C:\\Program Files\\Git\\git-bash"
}

Alt text Alt text

sublime安裝phpcs windows版

1.安裝sublime並安裝好package control

2.安裝phpcs

3.打開command並利用composer安裝 PHP_CodeSniffer PHP-CS-Fixer PHPMD

composer global require squizlabs/php_codesniffer fabpot/php-cs-fixer phpmd/phpmd

4.將PHP Code Sniffer => Settings - Default的內容複製到 PHP Code Sniffer => Settings - User

5.修改Settings - User

{
    "phpcs_executable_path": "phpcs.bat",
    "php_cs_fixer_on_save": true,
    "php_cs_fixer_executable_path": "php-cs-fixer.bat",
    "phpcbf_on_save": true,
    "phpcbf_executable_path": "phpcbf.bat",
    "phpmd_run": true,
    "phpmd_executable_path": "phpmd.bat"
}

如果進行存檔時還是會提示錯誤訊息修改下列參數即可

{
    "phpcs_php_prefix_path": "php路徑\\php.exe"
}

參考 wamp安裝 composer安裝

如何在windows下安裝composer

composer是目前最多人使用的php套件管理單, 只要下一個指令, 就可以將所有套件相依性的軟體下載, 並只要require一隻autoload.php即可

不過要安裝composer有些步驟得進行, 說明一下如何安裝composer

1.下載windows版composer下載 並安裝(安裝過程中,會要求指定php.exe資料夾) Alt text

  1. 開始右鍵 => 系統 => 進階系統設定 => 環境變數 Alt text

  2. 新增變數 變數名稱: PHP_HOME 變數值: PHP路徑(範例為C:\PHP) Alt text

  3. 新增變數 變數名稱: COMPOSER_HOME 變數值: C:\ProgramData\ComposerSetup Alt text

  4. 修改path 在原本的path後面新增 ;%PHP_HOME%;%COMPOSER_HOME%\bin;%COMPOSER_HOME%\vendor\bin ※由於已經設定%COMPOSER_HOME%所以往後利用 composer global require 套件 都會安裝至%COMPOSER_HOME%\vendor 如果套件內有執行檔則會自動安裝至%COMPOSER_HOME%\vendor\bin 所以%COMPOSER_HOME%\vendor\bin也要加入path變數之中

  5. 打開command模式,直接執行composer即可 Alt text

uniform server 在windows下體積小快速安裝的wamp server

在Windows裡安裝wamp server 除了常見到的xamppwampserver之外 還有這一套『uniform server』 uniform server標榜的是體積小 主程式安裝檔案也才27M 不過相對功能就比較少 安裝程式只包含了apache、php5.4、mysql 其他的phpmyadmin、mariadb等等都可以利用plugin的方式來進行安裝

至於安裝的方法,只要將檔案下載後,解壓縮到任一資料夾就可以使用了

以下為安裝步驟

  1. 下載檔案(只有27M) Alt text

  2. 點擊兩下解壓縮 Alt text

  3. 進入UniServerZ資料夾 Alt text

  4. 點擊UniController.exe(用系統管理員身份執行才能設定開機後自動執行wamp) Alt text

  5. 允許防火牆 Alt text

  6. 更改mysql root預設密碼,不更改可直接點選cancel(預設密碼為 root) Alt text

  7. 啟動apache及mysql Alt text

  8. 設定開機執行apache及mysql Alt text Alt text

  9. 這樣就設定完成了 Alt text

其他比較常用到的apache及php設定 1.設定apache rewrite 2.設定php curl 要更改apache及php的設得先停止apache

設定rewrite的方法 Alt text Alt text

設定php curl Alt text Alt text