本文實例講述了Laravel 5.1 on SAE環境開發方法。分享給大家供大家參考,具體如下:
Laravel-簡潔、優雅的PHP開發框架,為 WEB 藝術家創造的 PHP 框架,如今正式移植到SAE環境。
由于Laravel 5.1相比于Laravel 4有很多的改動,不僅以目錄結構更加清晰,而且功能也更豐富。但是Laravel官方還是沒有原生支持SAE環境(估計永遠不會支持),所以我就做了一個移植版本,可以很優雅的切換本地和SAE環境。
由于SAE的特殊性,那么這幾個核心問題就必須要解決
#1 putenv()函數禁用
#2 模板編譯
#3 緩存類
#4 日志處理
#5 Session類
#6 服務提供者緩存
#1 putenv()函數禁用
Laravel 5.1使用了這個putenv()函數來向當前的環境中動態添加變量,但是很遺憾的是SAE的PHPRuntime禁用了該函數,所以只能使用折中的方法來實現。當初本來想Hook掉該實現,后來覺得沒必要,這個函數在Laravel 5.1中主要是為了使用.env配置文件來統一團隊的配置。所以我是直接禁用了該功能,在vendor/vlucas/phpdotenv/src/Dotenv.php的86行左右,直接注釋掉該函數,然后把所有的配置信息都寫到config文件夾的相應配置文件中。雖然解決了該函數被禁用的問題,但是實現的不夠優雅,希望有大神可以給出更加優雅的實現。
#2 模板編譯
該問題主要還是因為SAE的本地環境寫入被禁止,所以我使用了Wrapper來把編譯后的模板文件寫入到Storage。本來是打算寫到KVDB中,但是會出現一些奇奇怪怪問題,原因不明。
在config\view.php文件中修改:
1
2
3
4
5
6
7
8
9
10
|
$compiled = [ 'paths' => [ realpath (base_path( 'resources/views' )), ], 'compiled' => realpath (storage_path( 'framework/views' )), ]; if (SAE){ $compiled [ 'compiled' ] = 'saestor://' .SAE_STORAGE. '/compiled' ; } return $compiled ; |
注意要在相應的Storage中建立compiled文件夾。
#3 緩存類
Laravel 5.1沒有直接提供SAE可用的Memcache緩存驅動,這個解決比較簡單,直接寫一個服務提供者注冊到app.php即可,然后在config\cache.php中注冊,具體實現看項目源碼
#4 日志處理
這也是一個比較棘手的問題,由于Laravel 5.1的日志處理已經不是和4一樣使用服務提供者,而且直接注入到啟動器中,這就使得我們只能覆寫原生ConfigureLogging啟動類,而官方也沒有給出如何覆寫和在哪里覆寫,所以我這邊的解決方案是判斷當前環境為SAE后直接重寫Http內核中的一個啟動器屬性,核心代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
namespace Illuminate\Cloud\SAE; use App\Http\Kernel as DefaultKernel; class Kernel extends DefaultKernel{ /** * The bootstrap classes for the application. * * @var array */ protected $bootstrappers = [ 'Illuminate\Foundation\Bootstrap\DetectEnvironment' , 'Illuminate\Foundation\Bootstrap\LoadConfiguration' , 'Illuminate\Cloud\SAE\Log\ConfigureLogging' , 'Illuminate\Foundation\Bootstrap\HandleExceptions' , 'Illuminate\Foundation\Bootstrap\RegisterFacades' , 'Illuminate\Foundation\Bootstrap\RegisterProviders' , 'Illuminate\Foundation\Bootstrap\BootProviders' , ]; } |
這樣還不行,還必須重寫日志的部分實現
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Writer extends IlluminateLogWriter { protected function useSaeLog( $level = 'debug' ){ $level = $this ->parseLevel( $level ); $this ->monolog->pushHandler( $handler = new SaeLogHandler( $level )); $handler ->setFormatter( $this ->getDefaultFormatter()); } public function useFiles( $path , $level = 'debug' ){ if (SAE) { return $this ->useSaeLog( $level ); } parent::useFiles( $path , $level ); } public function useDailyFiles( $path , $days = 0, $level = 'debug' ){ if (SAE) { return $this ->useSaeLog( $level ); } parent::useDailyFiles( $path , $days , $level ); } } |
#5 Session類
Laravel5.1的session依舊是本地寫的問題,參考了Laravel4的移植,使用了memcache作為session的實現,具體可以結合緩存部分來處理
#6 服務提供者緩存
在應用程序的啟動過程中,laravel會在bootstrap/cache/services.json生成服務提供者的緩存,為了加快下次訪問的速度,依舊是本地寫的問題,解決方案很簡單,使用Storage的Wrapper即可
以上這些問題解決后,差不多就算成功了。最后修改下bootstrap\app.php來實現本地與SAE環境的優雅切換,主要是判斷環境然后生成SAE專有應用實例和注入相應的Http內核。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
/* |-------------------------------------------------------------------------- | Create The Application |-------------------------------------------------------------------------- | | The first thing we will do is create a new Laravel application instance | which serves as the "glue" for all the components of Laravel, and is | the IoC container for the system binding all of the various parts. | */ define( 'SAE' ,true); define( 'SAE_STORAGE' , 'laravel' ); if (SAE){ $app = new Illuminate\Cloud\SAE\Application( realpath (__DIR__. '/../' ) ); $app ->singleton( Illuminate\Contracts\Http\Kernel:: class , Illuminate\Cloud\SAE\Kernel:: class ); } else { $app = new Illuminate\Foundation\Application( realpath (__DIR__. '/../' ) ); $app ->singleton( Illuminate\Contracts\Http\Kernel:: class , App\Http\Kernel:: class ); } /* |-------------------------------------------------------------------------- | Bind Important Interfaces |-------------------------------------------------------------------------- | | Next, we need to bind some important interfaces into the container so | we will be able to resolve them when needed. The kernels serve the | incoming requests to this application from both the web and CLI. | */ $app ->singleton( Illuminate\Contracts\Console\Kernel:: class , App\Console\Kernel:: class ); $app ->singleton( Illuminate\Contracts\Debug\ExceptionHandler:: class , App\Exceptions\Handler:: class ); /* |-------------------------------------------------------------------------- | Return The Application |-------------------------------------------------------------------------- | | This script returns the application instance. The instance is given to | the calling script so we can separate the building of the instances | from the actual running of the application and sending responses. | */ return $app ; |
這里解釋下為什么要在bootstrap\app.php中來定義是否為SAE環境,原因很明確了,就是要注入相應的應用程序實例和Http實例,然后再這里也定義一下Storage
然后就是config\app.php的相關配置,根據環境判斷來注入相應的服務提供者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
if (SAE){ $removeProviders = [ Illuminate\Cache\CacheServiceProvider:: class , Illuminate\Session\SessionServiceProvider:: class , ]; for ( $i = 0; $i < count ( $app [ 'providers' ]); $i ++){ if (in_array( $app [ 'providers' ][ $i ], $removeProviders )) { unset( $app [ 'providers' ][ $i ]); } } $app [ 'providers' ] = array_merge ( $app [ 'providers' ],[ Illuminate\Cloud\SAE\Cache\SaeCacheServiceProvider:: class , Illuminate\Cloud\SAE\Session\SessionServiceProvider:: class , Illuminate\Cloud\SAE\Storage\StorageServiceProvider:: class , Illuminate\Cloud\SAE\Segment\SegmentServiceProvider:: class , ]); $app [ 'aliases' ][ 'Storage' ] = Illuminate\Cloud\SAE\Storage\Storage:: class ; $app [ 'aliases' ][ 'Segment' ] = Illuminate\Cloud\SAE\Segment\Segment:: class ; } |
最后再說說SAE專有應用程序實例和Http實例與原生的差別,主要還是本地寫的問題。原生的會在應用程序啟動時候生成路由、配置、服務提供者、模板編譯的相關文件,以此來提升加載速度。但是到了SAE就不行了,所以重寫了Application類的部分與路徑相關的方法,來把這些文件生成到Storage中,而Http專有內核則是處理啟動器中的日志類。具體代碼就不貼出來,可以看看項目。
再給一個SAE可以使用的rewrite
1
2
3
|
handle: - rewrite: if (path ~ "^/$" ) goto "public/index.php" - rewrite: if (!is_dir() && !is_file() && path~ "^(.*)$" ) goto "public/index.php/$1" |
總結
整個移植過程還算是很順利,得益于Laravel的拓展性與SAE的便利.不過在對于putenv()函數和日志處理的解決方法上,還是實現的不夠優雅,希望能有人給出更有優雅的實現方案。然后其他的SAE服務比如分詞、郵件、隊列等,則可以使用服務提供者自動加載,這個就不多說了。
項目github地址: https://github.com/wh469012917/laravel5-on-sae
希望本文所述對大家基于Laravel框架的PHP程序設計有所幫助。