Questions about mezzio-swoole, everytime I refresh the page, title reduplicated one more time!

Pls check this webiste http://www.mm3bb.com/

It is a brand-new skeleton, every time I refresh the home page, the title repeat one more time.
like
<title>Home - mezzio</title> 1st time visit

<title>Home - mezzio - Home - mezzio</title> 2nd time visit

<title>Home - mezzio - Home - mezzio - Home - mezzio</title> 3rd time visit

That’s ridiculous! I don’t know what’s going wrong.

Anybody can tell me why?

Most probably the problem is that the template renderer is not stateless, but keeping bits of information inside of it.

When you serve an app with php-fpm, a new process is spinned-up for every request, which makes this kind of things pass unnoticed.

However, since swoole keeps services in memory and reuses them between requests, if you append a title on every request, they can end up getting merged.

This is documented and you can find some ways to work around it here: Considerations when using Swoole - mezzio-swoole - Laminas Docs

Thanks for your kind reply! But I am not familar with the mezzio-swoole framework. The swoole-kind framework works so much different from the php-fpm kind.

I am just trying to apply mezzio-swoole to do stuff. I didn’t think about that I will encounter problems when I just install the official framework skeleton.

In my plan, I first install the official skeleton, then change it little by little, at least I can edit the view, it should be safe.

I didn’t append a title to every request, If I remove this, then the webpage have no title at all. I don’t know how to deal with this.

This is the key. Swoole is quite new and works in a fundamentally different way. You must understand that difference in order to understand why you’re seeing this behavior. You’re likely to run into it again, if you’re going to use Swoole, so I recommend studying up on that. @acelaya gave a good overview and linked to a place to get started. Php is going stateless but it’s not going to happen overnight.

The head-title view helpers use a stateful container. I normally inject a middleware that resets all of the laminas view helpers affected for every request. Head title, head link, head script etc.

Here’s a link to something a bit old that I found - it refers to the Zend equivalents.

Where this middleware file locate?

It’s something I wrote for a private project that gets inserted into the main pipeline.

@george, thanks for your kind reply! However I still have problem with it.

There are a few things there that need looking at.

First of all, you’ve piped the middleware after the NotFoundHandler so it will never execute. Ideally, this middleware would execute either before or after every request that renders a view, so before DispatchMiddleware or after it would work.

You also don’t appear to have setup a factory for the middleware. You can see from its constructor that it has a dependency on the HelperPluginManager, so you will need to create a factory that looks something like:

class ResetViewHelpersFactory {
    public function __invoke(ContainerInterface $container) : ViewHelperResetMiddleware
    {
        return new ViewHelperResetMiddleware(
            $container->get(HelperPluginManager::class
        );
    }
}

(Namespaces and use statements omitted for brevity)

Don’t forget that you must “Wire up” the factory in your configuration under $config['dependencies']['factories']

Without a factory to correctly construct the middleware, you’d receive 'ServiceCantBeCreated` exceptions for every request.

Hope that helps!

@george

I followed your steps, maybe I missed something, still not work for me.

Silly me - you probably need to pipe before dispatch because your template renderer will likely return a response.

Also, you will need to import the HelperPluginManager in your factory - i.e. use Laminas\View\…

@george Thank you so much! It works!! Yes, you are right! When I move the middleware before the dispatch one in the pipeline, it finally works right.

Still I have a question, the viewhelperresetmiddleware is a uniform middleware which can be used all over the entire project. If there need a middleware only use in somewhere with a narrow scope inside the project, or just need to use in one controller, where should I register the dependency, and where should I inster the middleware? It should not be in the main pipeline because it is not a project-wide used middleware. Many thanks!

Glad you got it working :smiley:

Typically, what I do, and there are lots of ways to do it… I create an empty interface such as \App\Pipeline\SpecificCollectionOfMiddleware and then create a factory for it that returns a MiddlewarePipe - I think that’s a \Laminas\Stratigility\MiddlewarePipe if memory serves.

Once that is setup, I reference the interface name in the routes that are applicable to the pipeline, so inside your route configuration, you might have something that looks like $application>get(‘/path/to/thing’, SpecificCollectionOfMiddleware::class, ... route name etc);

Also because pipelines are middleware themselves, you can use this approach to create chunks of re-usable middleware chains if that makes sense?

@george

Thanks for your kindly reply!

What do you mean by promoting the conception “MiddlewarePipe”? Is it a special middleware? Or all the middleware are pipes? I am a little confused. I only get to know the pipline conception in django.
In my point of view pipe connected one after another to make the process flowing through.

Besides, in mezzio, the route arrange one reflect to another, not like laminas mvc framework, which can use route tree strategy to match a batch uri. Can I understand it like that?

When you setup a routes to show, create, edit or delete an album for example, inside your routing setup you might have something like this:

$app->get('/show', ShowAlbumHandler::class, 'show');
$app->get('/edit', EditAlbumHandler::class, 'edit');
$app->get('/delete', DeleteAlbumHandler::class, 'delete');

As things progress and you want to authenticate users of those routes, you’d need something like:

$app->get('/show', [OnlyAuthenticated::class, ShowAlbumHandler::class], 'show');
$app->get('/edit', [OnlyAuthenticated::class, EditAlbumHandler::class], 'edit');
$app->get('/delete', [OnlyAuthenticated::class, DeleteAlbumHandler::class], 'delete');

Perhaps later on you also want to restrict the IP address, but instead of prepending another middleware to all 3 routes, such as IPRestrictMiddleware::class, you create a pipeline something like this:


use Laminas\Stratigility\MiddlewarePipeInterface;
use Mezzio\MiddlewareFactory;

final class SomePipelineFactory {
    public function __invoke(ContainerInterface $container) : MiddlewarePipeInterface
    {
        $factory = $container->get(MiddlewareFactory::class);
        
        return $factory->pipeline([
            IPRestrictMiddleware::class,
            OnlyAuthenticated::class,
        ]);
    }
}

I personally like to create marker interfaces for specific pipelines that look something like:


use Laminas\Stratigility\MiddlewarePipeInterface;
interface SomePipeline extends MiddlewarePipeInterface
{
}

… so that in config I have

'dependencies' => [
    'factories' => [
        SomePipeline::class => SomePipelineFactory::class,
    ],
],

Finally, in my routing setup, I can say:

$app->get('/show', [SomePipeline::class, ShowAlbumHandler::class], 'show');
$app->get('/edit', [SomePipeline::class, EditAlbumHandler::class], 'edit');
$app->get('/delete', [SomePipeline::class, DeleteAlbumHandler::class], 'delete');

So, that’s what I meant - A convenient way of referring to and composing collections of middleware when you have multiple uses for the same functionality.