Conversations with Daniel

Table of contents

Loading...

Introduction

The disqus conversations on Daniel Opitz's blog posts are deleted in the blog but saved here for archival purposes.

The conversations date from 2020-2021.
In that time period I was just getting started with PHP and Slim. It was an early stage of my learning process.

Due to the conversion from disqus comments to markdown, line breaks and code blocks are not always correctly formatted.

Frontend development and asset management

From blog post Slim 4 - Twig Setup | Daniel’s Dev Blog

Main talking points:

  1. The benefits and drawbacks of using Webpack for managing assets in web development projects.
  2. The comparison between Symfony's asset management and twig-assets.
  3. The use of asset() function versus writing the full relative path directly into the Twig template.
  4. The discussion on the complexity of web development and the evolution of frontend development.
  5. The consideration of using Vue.js for your projects and the possibility of building applications with JavaScript and libraries like jQuery.
  6. The discussion on using slimphp/PHP-View as a renderer and the potential security pitfalls like XSS.
  7. The consideration of using CDN for libraries versus bundling them locally.
  8. The discussion on the necessity of certain libraries like symfony/http-foundation for Session-handling and cakephp query builder for database transactions.

Samuel

Hi Daniel

Great article again and thank you so much for sharing your precious knowledge and experience.

Right now, for me, I don't see enough benefits to use Webpack. How would you bring in assets (js / css / images) without webpack? While researching I found symfony/asset with the asset() function but then I saw that you did a library twig-assets. What is the difference with symfony/asset, is it a much lighter version of symfony/asset?

And is it really worth using asset() or is it also doable to write the full relative path directly into the twig template on bigger projects? What is your opinion on this?

Daniel

Hi Samuel.

The twig-assets component is more a lighter version of the Symfony Web Assets component.

The Symfony Asset component is a different and newer component from Symfony to manage assets. This component provides support for JSON manifest files (webpack).

Just some days ago Symfony released just another frontend component: Symfony UX New in Symfony: the UX initiative, a new JavaScript ecosystem for Symfony

So you have a lot of options today :-)

In my current projects I use Webpack in combination with a Twig extension called "fullpipe/twig-webpack-extension". This works very well for tiny and big projects as well.

Do you use Twig or Blade as Template engine?

Samuel

Alright.
On this question on why use the asset() function over hardcoded relative urls the answer was (how I understood) basically the ability to map asset-paths in a config file which makes asset moving easier to manage. Is that the "only" main advantage in your POV? And can twig-assets do that ? I'm using Twig because of the popularity, available doc, easy integration of translations and globals.

About Webpack. I get that when well configured it makes the job easier in many ways (like this article points out). And pages potentially faster since it suppresses excess calls since js and css files are bundled. In any case it is one layer of complexity more on top of the code, even if it makes the job easier later. I especially don't use a big framework like Symfony or Laravel because I then strongly rely on a specific codebase and if it updates, I have to change my code and configuration as well eventually if I like it or not, debugging is made harder, there are some restriction when doing specific actions (I had once a very specific issue and I could do it in normal env including js with <script>-tag but I had to do the project it in Symfony 4 with webpack and even the Symfony expert in our company could not bring in the js at the right time in my project), configuration is sometimes not easy especially if I need to do something that is not common and there are many more reasons why I prefer to work with the least amount of libraries possible. Exactly because of this I'm such a huge fan of Slim.

The thing that made me balance against Webpack were the comments of the article and what I could read about users elsewhere. Especially this comment from Layton Miller (if it doesn't jump to the comment you have to scroll on the bottom and load disqus and then reload) and this sentence: I now spend more time wrestling with Webpack than I do working on my projects and that is not a joke or an exaggeration. That is my nightmare if a pro has to wrestle with webpack how will it be for me? I prefer to invest a little more time when changing resource paths (and I have the help of my beloved IDE PHPStorm) and doing a manual shift + f5 than spending time messing with webpack because I have an error I can't find the cause or I can't do what I wanted to do and generally having the feeling that if an error occurs maybe I can't solve it.

Even Twig I made a long list of pros and contras and researched many hours. I like to code the nearest possible to the code that will be interpreted by the browser. Same with asset function. I want to use it only if it brings a lot of benefits and I can see myself "needing" it.

I would be very interested to know your opinion in your POV about this.

P.S. The article Slim 4 - Webpack is absolutely great and I think its fabulous that you shine light in the webpack configuration and usage with slim. That made me consider it strongly - but what if I, some time, need something that is slightly different, than what is in your doc? Will I be able easily or would I invest a lot of time and then wish that I never started with all this? I think I'm not ready yet to "risk" that.

Daniel

I think we both feel the same. I love minimalism and simplicity.

But complexity is always and everywhere the same. Complexity can't be removed, it can only be moved to another place where you cannot see it. Complexity is still there, even if you hide it under multiple layers of abstractions.

Every day I think about how to keep all this unnecessary things out of my tech stack. 15 years ago frontend development was much "simpler" because we included some js and css files and started hacking. The only "language" the Browser can understand is still HTML, JS, CSS.

In the last years SPA's became more and more popular to implement desktop-like web-applications for the browser. I don't like SPA's for multiple reasons. They break the fundamental principle of the web, they are slow, floppy, buggy, expensive to build and a usability fail. But anyway... today, building a React, Vue or whatever frontend framework of the week an SPA'ish application requires a build process to transpile/comple the TypeScript- and JSX files into JS that the browser can interpret and run. This was the "great moment" for Webpack and similar tools.

You should not add Webpack or a PHP based assets library when it makes no sense to you. Don't add useless complexity. Keep it simple.

But when you reach a point where you have to install JS dependencies regularly and don't want to update it by downloading JS files manually, then better use Webpack to download and bundle it into a small and fast JS/CSS file. Bundling is also better for SEO.

Starting a watcher process that recompiles the changed JS/CSS files again and again is a much complexer workflow. When the webpack compilation fails, the watcher process stops and has to be started again. I don't like this process, to be honest.

At the end I would try this approach:

  1. Don't install any PHP assets library.
  2. Just link your application specific assets in your templates like before and press F5 to reload it. These files must be placed under the public/ directory, e.g. public/js public/css.

There is one exception:

  1. Install webpack and use it to download and compile/bundle your external dependencies (like jQuery, Bootstrap, etc..) into a single JS file.
  2. Link this static file in your global layout template(s). You don't need a asset function for this.
  3. To update your external dependencies run "npx webpack --mode=production".

This approach is a "mix" of both worlds. It's much simpler and you still can develop without recompile the stuff again. So webpack is only used to download, update and bundle the JS packages from NPM. The rest works like before.

Samuel

First of all, I'm beyond grateful and can't thank you enough for the time you take to reply to me. It's so great to hear an opinion after hours of researching from someone I'm looking up to.

I love web development so much because it is so portable and light yet powerful and with huge possibilities. We have the ability to build complex and big applications that can be accessed by any device that has a browser (and internet access) and it doesn't even need horsepower since the workload is on the web servers and not the client. How great is that?

I feel like by building heavy SPA's we are crushing this huge advantage because like you said many are slow, buggy and expensive. Might as well just do a desktop app in this case. Excel is faster and more responsive (reaction time after mouse action) than Spreadsheets.

I planned to use Vue.js so yes, your suggested approach seems to fit my needs perfectly!

Is it considerable though to nowadays, build applications even complex bigger ones solely with JavaScript and libraries (like jQuery) so it doesn't have to be compiled? Or is it not conceivable and it doesn't make sense maintaining and developing large frontend-applications without SPA frameworks like vue, react, angular with TypeScript. Because as you say, complexity cannot be avoided and these tools are a great help.

To be clear, I'm asking in the perspective of dev something with quality that lasts long and is very fast for the client even if it's longer to code rather than trying to save time in the development process. Possible bugs and clean code are naturally also a criteria. I believe TypeScript wins against vanilla JS or jQuery on this one.

And to be honest I even planned considering removing twig and replace it with a renderer like PHP View. The reason I shifted towards twig was because of your excellent doc above (with the slim inclusion, translation and globals) and because I don't know how to implement translation with a PHP renderer (didn't try either but that is on my todo list) and didn't find much on google at the first glance. Other reason are the cool Twig-View methods url_for() (which uses the slim route names) and is_current_url and others. Right now I wouldn't exactly know how to work with those names properly. (Speaking about this, I would be highly interested in an equivalent doc like this above blogpost about twig but using a PHP-renderer. Probably it's not general interest and I'm the only one that would benefit from it)

Daniel

Yes, the PHP renderer would be a good next topic to write about.

If you use slimphp/PHP-View, you should consider some security pitfalls like XSS etc. You have to make sure that all the output is correctly encoded. But this is the only drawback. On the other hand, slimphp/PHP-View is native PHP and much more lightweight. Also text translations are easier than with Twig. The only question would be how to implement url_for etc., but even that is possible.

Frontend: In the long run, I would go as "native" as possible, because the frontend market changes about every 6 months ;-) Today I would not consider jQuery for building complex web-applications. A lot of jQuery features (e.g. selectors) a supported by the browser directly now. For this reason I am currently refactoring a large code base from jQuery to Vanilla-JS. My approach is to download the missing functionality (components, shims, polyfills) from NPM and bundle it into a static file. I download only the components I really need (e.g. Bootstrap, FontAwesome, Axios, SweetAlert2 etc...). This makes it faster and better maintainable in the long run.

Samuel

You don't know how happy it makes me to read that! I no longer feel insane because I want to go as native as possible and use the integrated technologies (PHP, HTML, JavaScript). It's truly what my intuition wants me to do, just plain JS but I never had the courage to consider it… Until now :) I have to take care of XSS, yes but since I was working on a stateless API before, I already have my escaping util. class and got used to escape every return values. Since escaping is such a killer feature I'm sure there are good reasons why slimphp/PHP-View doesn't include something like this it in the library.

One last question though, in school we learned that we should include the libraries (bootstrap, font awesome, jquery etc.) via CDN in production because it would be faster to load since the client most probably has them already cached from another website and google would prefer it. If I understood correctly, you suggested having them locally, bundle them with webpack and make the client load one JS files with all needed libraries right? I'm asking with the intension of doing deep web apps (so not findable on google, with a login) so I don't care about google, just what is the most efficient to dev and quickest for the client. If I have them locally I can update them all with "npx webpack --mode=production" thats faster than changing the links and I assume the bundled file is cached after one site load so for the next ones the browser can re-use its cache in which case it doesn't matter, right?

Daniel

If you need help regarding correct html encoding in PHP-View, please contact me. Maybe I will write a new article about this topic.

CDN: A CDN is probably? faster than your server, but I still would not use it. If your teacher tells you to use a CDN then use a CDN, but in all other cases I would try to avoid CDN's for multiple reasons like privacy (google etc), security (CDN Malware), network issues (DNS, DDoS etc) problems (happened multiples times in the past).

I would recommend to bundle them with Webpack and let the client load only your "own" JS files. These assets can only be cached per "browser" and not across multiple devices. Therefore, each client must download the same assets at least once to store them in its own local browser cache. We are talking about only a few kilobytes, so this is still very fast and your applications runs free from dependencies on other (spying) networks etc.

Samuel

I used htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') for normal outputs and didn't need more specific contextual escaping for now. Where I would need help with PHP-View is regarding the topics brought up in the above blogpost. Configuration and templating I can do but with translations and globals I'm very inexperienced to say the least as well as finding an url_for() equivalent. I would not come up with a clean solution in a short amount of time. Probably you have a better output escaping way than me as well. But all this is not so urgent, I can focus on other things first. I will wait until you made a blog post about this subject (if you want to do it naturally).

I want to have only the absolute necessary libraries, and I was considering removing symfony/http-foundation for Session-handling (for flash I thought about using a tiny library only for that like plasticbrain/PhpFlashMessages or do it myself).

And for database transactions well I used cakephp query builder but I feel like this can be easily swapped with plain PDO.

In your opinion, do these two bring enough value to be added when the goal is being as native as possible or not?

Daniel

The escaping with "htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')" is optimal. I would wrap this code into a function.

Session: Slim also provides a Flash component, but it requires a running session, which is tricky, because the session must be started first (in a middleware) and not within the dependency injection container. But it works well with a little trick.

PDO vs QueryBuilder: This is not a question about native or not, it's about security in this context. For learning purpose PDO is "OK". That's why I use it in my tutorials. But as soon as you want to write more complex and dynamic queries PDO becomes a pain and insecure mess, even with prepared statements. I would not use PDO in real projects. Better use a QueryBuilder instead, seriously.

Samuel

Cool! I will have a look at the flash component and start the session in a middleware first. But if you think that symfony sessions bring a lot of value and is not a library that should be removed from the tech stack let me know. I think I'll go with classic PHP sessions for now and if in the future I need something like memcached or redis I'll consider swap to http-fundation sessions. Edit: I ended up choosing your session/flash lib odan/session. I think its definitely value added to the project and better than something I would do.

PDO vs QueryBuilder: Thank you, this is what I needed to hear.

Asset Management with PHP Template Renderer

From blog post Slim 4 - PHP Templates | Daniel’s Dev Blog

Samuel

Hi Daniel
How would you include bundle-specific stylesheet files individually when using a layout without webpack?

I have a layout that I include with $this->phpRenderer->setLayout('layout.html.php'); in which I link the default css-files for the general page structure etc. that should be loaded on every request. Now I don't want to always include the style specific to each bundle. I don't want to include the CSS for user page, login etc. when a 404 error page is displayed to the user. I want that only the layout stylesheets and error.css get loaded. And when I'm on the user profile page I don't need the error.css but user.css and so on. Each template content is added via the $content var in the layout inside the -tag and to properly add a CSS file I would need to access the segment.

In Twig, we can use the handy block css {% block css %} {% webpack_entry_css 'login/login' %} {% endblock %} but how would I do this with PHP-View? Maybe use JavaScript to modify the head after the page loaded? Or include the content in a tag?

Edit: Asked here as well :)

Conversation continued in https://github.com/slimphp/PHP-View/issues/69 Daniel

Sections or “blocks” can be rendered using the addAttribute and fetch method. This example shows how to define the assets within a specific page and how to render it into the layout page:

<?php $this->addAttribute('js', ['page.js']); ?>
<head>
    <?= $this->fetch('js.php', ['assets' => $js ?? []]) ?>
</head>

The section template for the JavaScript assets, js.php:

<?php
foreach ($assets ?? [] as $asset) {
    echo sprintf('<script type="text/javascript" src="%s"></script>', $asset);
}

More details: https://odan.github.io/2020/12/09/slim4-php-view.html#sections

Logger while Testing

From blog post Slim 4 - Logging | Daniel’s Dev Blog

Samuel

I tried to mock it but get "Class "App\Domain\Factory\LoggerFactory" is declared "final" and cannot be mocked." Would you recommend creating a NullLoggerFactory that returns NullLogger for testing? But if I do I would have to create an additional LoggerFactoryInterface. Or should I remove the "final" keyword from the LoggerFactory and then mock it (and add to the container I use your infrastructure from Slim 4 - Testing) in the setUp() method. Seems like an easier and more efficient solution, or is there a good reason that the class is marked as final?

What is your opinion on "Make your classes always final, if they implement an interface, and no other public methods are defined" (source) since LoggerFacotry doesn't implement an interface, this sentence is leading towards removing the final keyword. Versus "Since it screws up mocking; consider creating interfaces or abstract classes for each "final" class." (source) which leads to create an interface for LoggerFactory rather than removing the final keyword.

Daniel

Hi Samuel!

To make a Factory testable I would go another way. I would keep the class final. An interface would be to complex here because the factory has multiple methods. I would try a more pragmatic way. I think there a 2 ways to make it more testable. The Chronos and the Guzzle approach.

Chronos uses a static method setTestNow to define a mocked system time.

For example: Chronos::setTestNow('2021-02-01 00:00:00');

So for the LoggerFactory it would look like this (it's not a static method):

public function setTestLogger(LoggerInterface $logger = null): void
{
if (!$logger) {
$logger = new Logger('testing');
$logger->pushHandler(new NoopHandler());
}

$this->testLogger = $logger;
}

Then you are able to disable the logging by returning a Logger with only a NoopHandler.

Test Case Usage:

protected function setUp(): void
{
$this->app = require __DIR__ . '/../../config/bootstrap.php';
$this->setUpContainer($this->app->getContainer());

// Disable logging for testing
$this->container->get(LoggerFactory::class)->setTestLogger();

// ...
}

Guzzle solved this issued by passing a mock handler via the constructor: https://docs.guzzlephp.org/en/stable/testing.html#mock-handler

I would go for the Guzzle approach. What is your favorite?

Samuel

Okay, thank you for the suggestions.
May I ask why you used

$logger = new Logger('testing');
$logger->pushHandler(new NoopHandler());

and not

$logger = new \Psr\Log\NullLogger();

Are there disadvantages or drawbacks I am missing out on?

I liked your implementation in slim4-skeleton where you create the logger and add the NoopHandler() in the local.testing config file. And in the factory constructor you set the attribute $testLogger to the logger with the NoopHandler which is returned in the createLogger function (if set). I would probably go with something similar than this if my current implementation is bad.

My current implementation is basically just mocking the factory in the setUp() function:

// Mock LoggerFactory so that createInstance() returns NullLogger
// addFileHandler() automatically returns a stub of its return type which is the mock instance itself
$this->mock(LoggerFactory::class)->method('createInstance')->willReturn(new NullLogger());

For this I had to remove the "final" keyword and I felt okay doing so because I read "Make your classes always final, if they implement an interface, and no other public methods are defined" (source). The factory doesn't implement an interface and in my opinion it makes actually sense to work with a stub of the LoggerFactory since we are unit testing.

Daniel

The NoopHandler is just a Monolog specific handler for testing purpose.

I was not aware that the "\Psr\Log\NullLogger" even exists. This one is even better than the Monolog "NoopHandler" or "NullHandler" because it doesn't add unnecessary overhead to the test suite. I am updating this article now.

I still try to stick with final classes as long as there is a good and easy way to test them. Also, I try to avoid mocking as much as possible because to me composition is a priority and I want to avoid at all costs that anyone gets the idea to extend a class. This is just my personal philosophy due to bad experiences with readability and maintainability of mocks.

Samuel

Alright! Thank you for your insight, I understand now.

^