Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is Yii 2.x Supported? #78

Open
sxnazirov opened this issue Dec 29, 2018 · 26 comments

Comments

@sxnazirov
Copy link

commented Dec 29, 2018

Thanks for author!
Can this be used to host a Yii 2.x web app?

@Alex-Bond

This comment has been minimized.

Copy link
Contributor

commented Dec 29, 2018

Thanks for author!
Can this be used to host a Yii 2.x web app?

Unfortunately, all Yii versions not supporting PSR-7. Maybe in Yii 3 but I checked a few days ago and it looks like no support in it neither.

@samdark

This comment has been minimized.

Copy link

commented Dec 31, 2018

@Alex-Bond

This comment has been minimized.

Copy link
Contributor

commented Jan 1, 2019

3.x supports PSR-7: https://github.com/yiisoft/yii-web/blob/master/composer.json#L68

@samdark How I can start the app with PSR request? Do you have example?

@samdark

This comment has been minimized.

Copy link

commented Jan 1, 2019

By calling $app->handleRequest($request).

@Alex-Bond

This comment has been minimized.

Copy link
Contributor

commented Jan 2, 2019

By calling $app->handleRequest($request).

Ok. Will try within next few days. If it works @wolfy-j will add a wiki page.

@charlesportwoodii

This comment has been minimized.

Copy link

commented Jan 5, 2019

For Yii2 I have a very early working PSR-7 bridge I've been working on for a while that bridges a Psr\Http\Message\ServerRequestInterface to yii\web\Request and yii\web\Response back to a Psr\Http\Message\ResponseInterface for consumption by PSR-7 servers like RoadRunner.

It's available at: https://github.com/charlesportwoodii/yii2-psr7-bridge

At the moment it can mostly run the yii2-basic-app without any issues, but there's still a few things that need to be worked out on it. Pull requests are welcome to improve it.

@wolfy-j

This comment has been minimized.

Copy link
Member

commented Jan 5, 2019

@charlesportwoodii have you tried to bench with keep-alive on?

@charlesportwoodii

This comment has been minimized.

Copy link

commented Jan 5, 2019

@wolfy-j Yes and no.

siege -R <(echo connection = keep-alive) burns through sockets when keep-alive is configured, which causes siege to abort the test only after a few seconds. Since the metrics gathered were mainly to determine if the work-effort was worth pursuing or not, I didn't dive in much further since I was testing in non real-world conditions (both siege and rr running on the same machine, testing over lo). For real benchmarks I'd probably test against a remote server and with a few other tools.

Once the library is a bit more complete I plan on doing some some actual benchmarking in real-world conditions.

@Alex-Bond

This comment has been minimized.

Copy link
Contributor

commented Jan 10, 2019

Update: i tried to connect RR to Yii3. There some problems with integration. I already contacted @samdark about them.

@Alex-Bond

This comment has been minimized.

Copy link
Contributor

commented Jan 11, 2019

This is worker file for Yii3 is my fix (@yiisoft/yii-web#40) will be merged:

<?php
require __DIR__ . '/vendor/autoload.php';

use hiqdev\composer\config\Builder;
use yii\di\Container;
use yii\helpers\Yii;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;

$relay = new Spiral\Goridge\StreamRelay(STDIN, STDOUT);
$psr7 = new Spiral\RoadRunner\PSR7Client(new Spiral\RoadRunner\Worker($relay));
$httpFoundationFactory = new HttpFoundationFactory();

while ($req = $psr7->acceptRequest()) {
    try {
        $container = new Container(require Builder::path('web'));
        Yii::setContainer($container);
        /** @var \yii\web\Application $app */
        $app = $container->get('app');

        $request = $httpFoundationFactory->createRequest($req);
        /** @var \yii\web\Request $yiiReq */
        $yiiReq = $container->get('request');
        $yiiReq->setMethod($request->getMethod());
        $yiiReq->setUri($req->getUri());
        $yiiReq->setServerParams($request->server->all());
        $yiiReq->setProtocolVersion($request->getProtocolVersion());
        $yiiReq->setCookieParams($req->getCookieParams());
        $yiiReq->setQueryParams($request->query->all());
        $yiiReq->setAttributes($request->attributes->all());
        $yiiReq->setParsedBody($req->getParsedBody());
        $req->getBody()->rewind();
        $yiiReq->setBody($req->getBody());

        $container->set('request', $yiiReq);
        $response = $app->handleRequest($yiiReq);
        $psr7->respond($response->send(true));
    } catch (\Throwable $e) {
        $psr7->getWorker()->error((string)$e);
    }
}
@Alex-Bond

This comment has been minimized.

Copy link
Contributor

commented Jan 17, 2019

@wolfy-j @sxnazirov
Small update - so, the actual problem with Yii3 is much deeper than i expected.
I recommend closing this issue for now with resolution "Not possible at this moment, ETA unavailable".
I keep working on pull requests to Yii3 to fix some fundamental problems that prevent us from using it with RR.

As of Yii2 - i believe it will have the same problem with resetting data between requests.
@charlesportwoodii we probably need to talk in order to verify your solution. As i know Yii2 don't have terminate process as well.

Tech info:
At this moment Yii using too much direct strout printing. I'm working on rewriting request handling and error processing (at this moment most of the error writes to stdout, not response).
As the second problem that right now on in backlog - terminating request. At this moment Yii doesn't have any proper termination sequence, so data between requests will be not reset.

@wolfy-j

This comment has been minimized.

Copy link
Member

commented Jan 17, 2019

Agreed, we can come back to this issue once Yii3 is released. Current answer is - NO, it's not possible.

@wolfy-j wolfy-j closed this Jan 17, 2019

@samdark

This comment has been minimized.

Copy link

commented Jan 17, 2019

@Alex-Bond is working on it for 3.0.

@charlesportwoodii

This comment has been minimized.

Copy link

commented Jan 17, 2019

@Alex-Bond

For the Yii2 bridge I mentioned, the issues you mentioned (Yii2's error handler and resetting the application) are solved.

The main loop with the bridge presently looks as follows:

while ($request = $psr7->acceptRequest()) {
    try {
        // The application state is automatically reset at start of each request. `handle` also calls `\yii\Psr7\web\Application::terminate()` to flush logs and detach Yii2's global events.
        $response = $application->handle($request);
        $psr7->respond($response);
    } catch (\Throwable $e) {
        $psr7->getWorker()->error((string)$e);
    }

    // Automatically stop the worker if it approaches 90% of the defined memory limit to avoid crashes due to memory leaks
    if ($application->clean()) {
        $psr7->getWorker()->stop();
        return;
    }
}

With the configuration looking like:

env:
  YII_DEBUG: true
  YII_ENV: dev
  YII_ALIAS_WEBROOT: ./web/
  YII_ALIAS_WEB: 'http://127.0.0.1:8080/'
http:
  address:   0.0.0.0:8080
  workers:
    command: "php ./roadrunner"
    pool:
      numWorkers: 1
static:
  # root directory for static file (http would not serve .php and .htaccess files).
  dir:   "web"

  # list of extensions for forbid for serving.
  forbid: [".php"]

I have a running checklist detailing the current progress at the bottom of https://github.com/charlesportwoodii/yii2-psr7-bridge. Excluding a few items such as bootstraped components and some session issues, everything else works for the most part. These issues are only a problem for web apps since you can work-around them in RESTful API's.

This bridge is still pretty early, but at least for Yii2 the problem seems solvable.

@samdark

This comment has been minimized.

Copy link

commented Jan 17, 2019

That's great for 2.0. Keep up digging. For 3.0 it's a good way to check if framework is ready to work in such mode.

@charlesportwoodii

This comment has been minimized.

Copy link

commented Jan 17, 2019

@samdark

I think the only major issue is going to be around yii\web\Session. Yii::$app->getSession->close() doesn't seem to do the trick as I would expect. yii\web\CacheSession or a custom session handler could probably solve that problem - I just haven't gotten there yet.

Aside from that, the only real "annoyances" right now are:

  • YII_BEGIN_TIME is defined by BaseYii, so the "request time" constantly increases. It's not really an issue since I can use a PSR-15 middleware to properly measure that, it's just annoying in yii2-debug.
  • memory_get_usage() calls will report the memory usage of the worker, rather than the individual request. Again, more of annoyance with yii2-debug than anything else.
  • On each request the bridge effectively needs to run preInit, Component::__construct($config) and bootstrap(). Performance could be improved and memory usage could be lowered if there was an easy way to re-use existing components and modules instead of re-initializing them. The memory usage is low enough though we can just tell RR to kill the worker if it gets within 10% or so of the max memory so it's not a deal breaker.
  • The bridge has to de-register events to prevent duplicate registrations (eg yii-debug binding the debug bar hundreds of times on the page). It would be nice to have a $app->off() that de-registered every event associated to all components, or maybe a way to see all events that are attached to all objects for manual de-registration. Custom events that get registered are presently programmatically inaccessible. It would be up to the developer to know to de-register their custom events.
  • With something like Slim I can inject a PSR-15 middleware per route. The bridge can run a one-off middleware via an ActionFilter but obviously it can't run as part of a larger dispatcher chain. That's more of a nice-to-have than a need though.

Everything else either just works or I have a work-around for. I'll give another update once I'm a bit further along.

@Alex-Bond

This comment has been minimized.

Copy link
Contributor

commented Jan 18, 2019

@charlesportwoodii that you for a clarification! I checked your code but didn't find how you reset database connections. Can you point me?

@charlesportwoodii

This comment has been minimized.

Copy link

commented Jan 18, 2019

@Alex-Bond,

That's one of those not-yet-implemented but I have a plan for tasks. = )

It'll probably go here once I add it. Yii2 doesn't enable the db component by default, and it can be named something other than db, so I just need to search through the components for instances of yii/db/Connection and manually call yii/db/Connection::close() on them after I find them.

@wolfy-j

This comment has been minimized.

Copy link
Member

commented Jan 18, 2019

I'm going to highlight this ticket in README in case if someone else can help.

@wolfy-j wolfy-j reopened this Jan 18, 2019

@charlesportwoodii

This comment has been minimized.

Copy link

commented Jan 21, 2019

Minor updates:

  1. \yii\db\Connection instances are identified and closed at the end of each request: https://github.com/charlesportwoodii/yii2-psr7-bridge/blob/master/src/web/Application.php#L160.
  2. I found a way to get \yii\web\Session to work with the PSR-7 response object and play nicely with ext/session. All the normal extensions of yii\web\Session should work out of the box since there aren't any custom classes involved in that.

I also pushed out an instance of yii-app-basic with a sqlite user database for people to work with.

Functionally this is probably ~90% complete aside from test cases. There's a couple of small changes I'll work on in the next few days time pending. All the big ticket items seem to be working now with yii-app-basic and a separate JSON API I've been working with.

@free6k

This comment has been minimized.

Copy link

commented Jul 31, 2019

@charlesportwoodii, good job, you are is big lad!

@SamMousa

This comment has been minimized.

Copy link

commented Aug 9, 2019

@charlesportwoodii your method of checking connections assumes they are all components of the main application; they could easily be inside modules as well.

The issues you run into are approximately the same as they are in a testing framework (I've been rewriting the Yii2 Codeception connector on and off over the past year)
A cleaner approach could be this one: https://github.com/Codeception/Codeception/blob/3.0/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php

It used an event based approach.

The rest of the application is reset here:
https://github.com/Codeception/Codeception/blob/3.0/src/Codeception/Lib/Connector/Yii2.php#L107

Just in case it might be of use to you!

@charlesportwoodii

This comment has been minimized.

Copy link

commented Aug 9, 2019

@SamMousa Good point on the database connections. I'll be sure to take a look at the reset Codeception uses to see if other items can be incorporated.

Thanks for the tip!

charlesportwoodii added a commit to charlesportwoodii/yii2-psr7-bridge that referenced this issue Aug 13, 2019

Fixes event related issued
- De-registers all events at the end of each loop
- Closes database connections for all components (including modules) at the end of each loop
- Resets YII_BEGIN_TIME if uopz is installed.

Inspiration from spiral/roadrunner#78 (comment).
@charlesportwoodii

This comment has been minimized.

Copy link

commented Aug 13, 2019

@SamMousa

Thanks again for the tip on the connections that I overlooked. I'm implemented a similar solution that closes database connections for associated modules.

For anyone else following along at home, I've also updated the project to include a better event handler that properly de-registers every event at the end of the event loop. In previous implements events not manually offed would be duplicated until the process was finally terminated. This is fixed now - at least in the Yii2 App Basic package which I've updated. I plan on throwing a larger project like Craft3 which I had previous trouble with at it to see if it's fully resolved there so that even larger projects can take advantage of it.

@samdark

This comment has been minimized.

Copy link

commented Aug 13, 2019

@charlesportwoodii Yii 3 with Spiral should go way smoother because of PSR-7 and middleware. Validating that it works is on my too long TODO list :)

@samdark

This comment has been minimized.

Copy link

commented Aug 14, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants
You can’t perform that action at this time.