云计算开发者社区

用新浪微博连接

一步搞定

查看: 11717|回复: 15

Laravel 5.1 on SAE [复制链接]

Rank: 8Rank: 8

发表于 2015-9-7 10:34:10 |显示全部楼层
本帖最后由 Sun_翁航 于 2015-9-7 10:38 编辑

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文件中修改:

$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内核中的一个启动器属性,核心代码:

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',
  ];
}

这样还不行,还必须重写日志的部分实现

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内核。

/*
|--------------------------------------------------------------------------
| 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的相关配置,根据环境判断来注入相应的服务提供者

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

handle:
  - rewrite: if (path ~ "^/$") goto "public/index.php"
  - rewrite: if(!is_dir() && !is_file() && path~"^(.*)$") goto "public/index.php/$1"

总结
整个移植过程还算是很顺利,得益于Laravel的拓展性与SAE的便利.不过在对于putenv()函数和日志处理的解决方法上,还是实现的不够优雅,希望能有人给出更有优雅的实现方案。然后其他的SAE服务比如分词、邮件、队列等,则可以使用服务提供者自动加载,这个就不多说了。

项目地址:Laravel5.1 on SAE
演示地址:Laravel5

原文地址:Laravel 5.1 on SAE

作者博客:夏天的风



使用道具 举报

Rank: 9Rank: 9Rank: 9

发表于 2015-9-8 13:44:09 |显示全部楼层
感谢分享!赞!

使用道具 举报

Rank: 8Rank: 8

发表于 2015-9-8 22:43:12 |显示全部楼层
zhaixiaohu 发表于 2015-9-8 13:44
感谢分享!赞!

帮忙推广下,让大家一起来维护!

使用道具 举报

Rank: 9Rank: 9Rank: 9

发表于 2015-9-9 10:43:56 |显示全部楼层
Sun_翁航 发表于 2015-9-8 22:43
帮忙推广下,让大家一起来维护!

恩,一定的!

使用道具 举报

Rank: 13Rank: 13Rank: 13Rank: 13

发表于 2015-9-12 17:50:16 |显示全部楼层
赞,我也弄了个5.6的php装了个laravel跑脚本任务,不过还没细致移植测试所有功能。
putenv这个地方我也是注释掉了,但是修改了代码不影响env的使用。日志方面兼容支持SAE的日志流,缓存支持了SAE默认缓存,模板暂时只是将路径改到临时目录,能够运行,但不支持模板编译缓存

使用道具 举报

Rank: 8Rank: 8

发表于 2015-9-13 08:37:23 |显示全部楼层
蓝天白云oy 发表于 2015-9-12 17:50
赞,我也弄了个5.6的php装了个laravel跑脚本任务,不过还没细致移植测试所有功能。
putenv这个地方我也是注 ...

嗯,putenv注释掉是不能够够使用ENV函数的。模板的话写到临时目录这个每次请求都要重新生成,性能会下降

使用道具 举报

Rank: 13Rank: 13Rank: 13Rank: 13

发表于 2015-9-13 18:17:11 |显示全部楼层
Sun_翁航 发表于 2015-9-13 08:37
嗯,putenv注释掉是不能够够使用ENV函数的。模板的话写到临时目录这个每次请求都要重新生成,性能会下降 ...

修改下env函数就可以支持在sae上使用env了,这是我的修改示例。模板写临时目录只是我的临时策略,后续可以把模板写mc里的
function env($key, $default = null)
  {
    $value = getenv($key);

    if ($value === false && isset($_ENV[$key])) {
    $value = $_ENV[$key];
    }

    if ($value === false) {
    return value($default);
    }

使用道具 举报

Rank: 1

发表于 2015-10-2 00:51:57 |显示全部楼层
Class 'Illuminate\Cloud\SAE\Application' not found

为啥github里面木有vendor目录= =

使用道具 举报

Rank: 8Rank: 8

发表于 2015-10-5 13:54:32 |显示全部楼层
381613626 发表于 2015-10-2 00:51
Class 'Illuminate\Cloud\SAE\Application' not found

为啥github里面木有vendor目录= =

去原文地址查看,vendor太大了,上传很慢

使用道具 举报

Rank: 1

发表于 2015-10-14 14:11:03 |显示全部楼层
Laravel-简洁、优雅的PHP开发框架

使用道具 举报

Rank: 1

发表于 2015-10-28 13:48:46 |显示全部楼层
Fatal error: Class 'Illuminate\Cloud\SAE\Application' not found in /data1/www/htdocs/515/benim/1/bootstrap/app.php on line 18.
这个咋办?从hithub下载的,没有这个目录。

使用道具 举报

Rank: 8Rank: 8

发表于 2015-10-28 18:45:14 |显示全部楼层
LiwayidinQurban 发表于 2015-10-28 13:48
Fatal error: Class 'Illuminate\Cloud\SAE\Application' not found in /data1/www/htdocs/515/benim/1/boo ...

去我博客,github的不完整

使用道具 举报

Rank: 1

发表于 2015-10-30 09:23:31 |显示全部楼层
在你博客的链接也是导到 github 的啊

使用道具 举报

Rank: 8Rank: 8

发表于 2015-10-30 23:33:23 |显示全部楼层
Nefmoon 发表于 2015-10-30 09:23
在你博客的链接也是导到 github 的啊

再次查看

使用道具 举报

Rank: 1

发表于 2016-1-10 23:39:43 |显示全部楼层
不是有人写了将laravel发布到sae上的扩展包了?

使用道具 举报

Rank: 1

发表于 2017-1-20 14:37:16 |显示全部楼层
:):)

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

Archiver|云计算开发者社区 ( 京ICP证000007-137 )

GMT+8, 2017-9-24 23:54 , Processed in 0.201883 second(s), Total 12, Slave 11 queries .

Powered by Discuz! X2

© 2001-2011 Comsenz Inc.

回顶部