Categories
Resources Web Development

Symfony caching resource list and information

Once I started digging into Symfony caching I found all kinds of information all over. I’ll use this page to catalog it all for myself and others. This way I can quickly find what I am looking for.

Cache – From the docs. This is the overall plain documentation about Symfony cache. It covers the following

Configuring Cache with FrameworkBundle
Creating Custom (Namespaced) Pools
Custom Provider Options
Creating a Cache Chain
Using Cache Tags
Clearing the Cache
Encrypting the Cache

The Symfony Cache component – This is the actual caching component documentation. It covers the following.

Installation
Cache Contracts versus PSR-6
Cache Contracts
Available Cache Adapters
Generic Caching (PSR-6)
Basic Usage (PSR-6)
Advanced Usage

Cache pools and Adapters – from the documentation. This covers cache adapters such as Redis and Memcached. It covers the following information.

Creating Cache Pools
Using the Cache Contracts
Using PSR-6
Looking for Cache Items
Saving Cache Items
Removing Cache Items
Pruning Cache Items

Cache items – from the documentation. You need ItemInterface and cache items in order to set expire information on cached items. This link covers the following :

Cache Item Keys and Values
Creating Cache Items
Cache Item Expiration
Cache Item Hits and Misses

Categories
Resources Web Development

How to install and configure Redis in Symfony 5+ for local testing

Installing and configuring Redis for Symfony takes quite a few steps. So many I’d never remember them all. This article is for myself at a later date as well as anyone else who finds it useful. I’ll be updating this article as I learn more.
This article covers installing and configuring Redis for use for both Session storage and Application cache.

First you need Redis the program itself running. I suggest using Docker so you can quickly spin up Redis containers for experimenting. If you are not familiar with Docker I suggest you start with this getting started guide.

If you are using docker once you have started a Redis instance test it by trying to use the cli like so

redis-cli

You should see something similar to this.

127.0.0.1:6379

This means redis is running on 127.0.0.1 (localhost) on port 6379 which is the default port.
With redis-cli running you can further test with the following.

//set a key and value
set someKey "some value for the key"
//get the value for the key
get someKey
//view a list of all keys in redis storage
keys "*"

In production you need to install Redis or have access to a server running Redis, I’ll cover that in another article.

Install phpredis extension

You will need to install phpredis php extension and configure it. Before you can even do that though, you will need to install another php module php-dev I am using php 7.4 and Ubuntu so to install that I do this.

apt-get install php7.4-dev

This is needed because phpredis use phpize and phpize is included in php-dev.

If you are using another version of php you can search apt repository for this package like this:

apt search php-dev
or for version specific like this apt search phpver-dev
apt search php7.4-dev

Change the version number to match yours.

Next you install the phpredis extension from pecl.

pecl install redis

This is just the extension for the client to interact with your Redis server wherever it is, either local or remote.

Configure PHP

Now you must configure PHP  to use this extension.
You could add the needed config values to the php.ini config, but the problem is there are two. Yeah one for the cli and one for fpm. I have an easier solution. Create one file and symlink for both cli and fpm.

You can do it…

You will need both configured. As I found out if you configure only fpm your app will work, but when you go to composer install/update/require etc. you will get a cli error about missing such and blah Redis extension blah blah.

If you are running PHP 7.4 on Linux you will want to create a file in the following /etc/php/7.4/mods-available directory  named phpredis.ini  with the following

extension=redis.so
session.save_handler = redis
session.save_path = "tcp://localhost:6379?timeout=3&read_timeout=3"

You can find more info here in the phpredis docs.

Once you have created that file you need to symlink to the fpm and cli to let them know the configuration exists.

Run the following commands to symlink.


ln -s /etc/php/7.4/mods-available/phpredis.ini /etc/php/7.4/cli/conf.d/phpredis.ini
ln -s /etc/php/7.4/mods-available/phpredis.ini /etc/php/7.4/fpm/conf.d/phpredis.ini

The way this works is php after it reads the php.ini reads in all of the configuration files ( those with .ini extension) from the conf.d directory for either cli if you are using the command line or from fpm for your app. This makes configuring anything you need for php easier than having to open the giant php.ini file, plus you don’t have to worry about ruining one, which I have done easily.  Here is a link to the php docs on configuring and .ini files

Now you must restart php fpm for your app to work. On Ubuntu you can do this.

service php7.4-fpm restart

Configure Symfony for Redis Sessions

Now you must configure some things in Symfony. Part of the following can be found in the docs about caching in a Redis Database here.
From the docs you can see you need set these values inside services.yaml which is in the config directory of your app.


/yourapp/config/services.yaml
services:
    # ...
    Redis:
        # you can also use \RedisArray, \RedisCluster or \Predis\Client classes
        class: Redis
        calls:
            - connect:
                - '%env(REDIS_HOST)%'
                - '%env(int:REDIS_PORT)%'

            # uncomment the following if your Redis server requires a password
            # - auth:
            #     - '%env(REDIS_PASSWORD)%'
     Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler:
       arguments:
         - '@Redis'

The values for REDIS_HOST, REDIS_PORT and REDIS_PASSWORD should be defined in environmental variables on your system or in .env or in the secrets vault. For testing .env.test.local works.

I told you there were lots of steps, there is still more

Now there is still a little more configuring as the docs show in the link above. You need to configure the framework to use Redis for session storage. Open framework.yaml located in config/packages/ and change the handler_id and comment out the save_path file location info like so.


session:
        enabled: true
        handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler
        #save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
        cookie_secure: auto
        cookie_samesite: lax

Configure Symfony Cache to use Redis

You also need to configure the cache now in cache.yaml if you want to use Redis as a cache for your app.  You could configure everything in framework.yaml but it becomes a mess if you do that. Symfony reads all the files recursively located in the config directory, just make sure your yaml structure is correct.

If you open /yourapp/config/packages/cache.yaml you should see something similar already there.


/yourapp/config/packages/cache.yaml
framework:
    cache:
        # Unique name of your app: used to compute stable namespaces for cache keys.
        prefix_seed: sogizmo

        # The "app" cache stores to the filesystem by default.
        # The data in this cache should persist between deploys.
        # Other options include:

        # Redis
        app: cache.adapter.redis
        default_redis_provider: 'redis://%env(REDIS_HOST)%:%env(REDIS_PORT)%'
        #default_redis_provider: redis://localhost:6379
        # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
        #app: cache.adapter.apcu

        # Namespaced pools use the above "app" backend by default
        #pools:
            #my.dedicated.cache: null

Un-comment the lines shown under Redis section. You will notice a special syntax I am using. I kept messing around until it worked. You might not need to configure the default_redis_provider I need to do more research on that because it seems like that should be covered from the configs above, seems redundant.

default_redis_provider: 'redis://%env(REDIS_HOST)%:%env(REDIS_PORT)%'

That builds the string needed for the configuration basically this ‘redis://localhost:6379’  More about caching here in this Symfonycast.  that entire symfony cast is a great explanation of how the environmental system and cache works. More info about the string to connect to a redis provider here in the docs.

Using Symfony Cache in Controllers

This is actually the easy part, but instead of explaining it here I’ll write another article and link to it here, this article is too long already.

Links

Configuring symfony – link to the docs about configuring symfony .env file etc.

Symfony secrets vault – link to the docs about the secrets vault and keeping sensitive information safe in symfony.
Docker getting started guide.

phpredis extension and how to install and use docs

Symfony docs store sessions in a database -> includes Redis example

CacheInterface Symfony docs about caching items you need ItemInterface when you want to set an expires time for an item.

PSR6 CacheItemInterface documentation explaining this cache interface which Syfony ItemInterface uses.

Redis cache adapter docs – the documentation about configuring the redis cache adapter.

Categories
Resources Web Development

Symfony 5+ check if user is logged in inside a twig template

Often you may need to know whether a user is logged in or not inside a template to show or not show something. For example you might want to show links to login or register if a user is not logged in but show a link to logout if the user is logged in.

To do this you use is_granted() within a template with one of the following.

IS_AUTHENTICATED_ANONYMOUSLY, IS_AUTHENTICATED_REMEMBERED, IS_AUTHENTICATED_FULLY

<div class="modal-body">
<ul class="nav flex-column">
{% if is_granted('ROLE_SUPER_ADMIN_1') %}
<a class="nav-link" href="{{ path('show_dash') }}">Dashboard</a>
{% endif %}
{% if is_granted('ROLE_USER') %}
<a class="nav-link" href="{{ path('app_logout') }}">Logout</a> {% else %}
<a class="nav-link" href="{{ path('app_login') }}">Login</a> or <a class="nav-link" href="{{ path('app_register') }}">Signup</a> {% endif %}
</ul>
</div>

Using ROLE_SUPER_ADMIN_1 which is something I made up for my own app to check what type of admin the user is. I don’t really like the IS_AUTHENTICATED_* methods, read more about them in the link below if you want.

Link to more information about IS_AUTHENTICATED_* here in  a really old symfony cast I found via google.

Categories
Resources Web Development

PHP resource links

What’s New in PHP 8 (Features, Improvements, and the JIT Compiler)

Categories
Software Development Web Development

How to remove unused or broken docker container images.

Sometimes we make mistakes. When first learning docker we probably make many mistakes and end up with tons of unused docker images.

If you are on Linux like me, you won’t have a desktop dashboard like Mac and Windows get, so things are harder. To see a list of what images you have created you use the following command

docker ps -a

That command will output something like this.
It will show the CONTAINER ID, IMAGE, COMMAND etc. as you can see. To delete an image you use the container id with docker rm like this.

docker rm b5f8fae52bce

I’ve seen older internet posts using the IMAGE value but I had no success with that method. I am guessing something changed. I didn’t even see an example of this I just tried it. I don’t see any mention of this in the docs either. But it works. This part in the getting started intro actually explains it. I think something did change.

Docker container rm documentation.

Docker rm documentation.

Categories
Resources Software Development Web Development

HTTP headers and caching resources.

Resources all about HTTP headers and caching.

Caching tutorial -> great article to start with, explains all the basics of caching.

Hypertext Transfer Protocol (HTTP/1.1): Caching  rfc spec

Categories
Resources Web Development

How to create a cookie in Symfony 5.0+ and render a template in a controller

First what I wanted to do was create a cookie in a Controller and display a template at the same time. Sort of like when a user visits a page you set a page count or something. The documentation doesn’t really show an example, you are expected to know it via “common sense” apparently according to one smartass.

There is more than one way I have discovered over time. Apparently you can use render the same way I show using renderView.

Below is the Symfony Cookie class create method comment/documentation. This is all of the values you can supply when creating a cookie.

 /**
     * @param string                        $name     The name of the cookie
     * @param string|null                   $value    The value of the cookie
     * @param int|string|\DateTimeInterface $expire   The time the cookie expires
     * @param string                        $path     The path on the server in which the cookie will be available on
     * @param string|null                   $domain   The domain that the cookie is available to
     * @param bool|null                     $secure   Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS
     * @param bool                          $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
     * @param bool                          $raw      Whether the cookie value should be sent with no url encoding
     * @param string|null                   $sameSite Whether the cookie will be available for cross-site requests
     *
     * @throws \InvalidArgumentException
     */

If you create a cookie like this :

$response->headers->setCookie(Cookie::create('foo', 'bar'));

Then the cookie will only live/exist until the user closes their browser(unless your browser restores from your last session). You must supply an expires time to make it persist beyond closing the browser. Providing an expires time gives you better control over when the cookie expires due to the above mentioned browser restore issue which will restore cookies that should have died on browser close.

You can also create the cookie then pass it to setCookie() like this.

 $response = new Response();
        $expires = time() + 36000;
        $cookie = Cookie::create($cookieName, $cookieValue,  $expires);
        //$cookie = $response->headers->setCookie(Cookie::create('foo', 'bar'));
        $response->headers->setCookie($cookie);

        $content = "<html><body><h1>Learning symfony cookie creation techniques?</h1></body></html>";
        $response->setContent($content);
        $response->headers->set('Content-Type', 'text/html');
        return $response;

Here I set the expires to a number,  time() returns a linux/unix timestamp and I added 36000 seconds or 10 hours to it. This cookie will exist until the user refreshes their page or clicks a link in 10 hours from creation. However long you want it to live you add that many seconds. Or you could create a date using PHP DateTime as you can pass a DateTime object to the expires position. You then use the methods of DateTime to increase the time to a period in the future and pass the DateTime object after calling the methods to do so.

Side Note : in the above code, you can create a cookie without the $response->setContent() call. I do that with the body tag so that the profiler will show up at the bottom of the page for debugging.

That code goes inside a controller method for the requested route by the way. Usually you use the render() method inside a controller to send a response, which renders the template and sends it in a response. You can also use renderView to do the same thing and capture the value in a variable then use setContent or just make the renderView call right in setContent. I know that works. You can also store the returned value from render the same way.  But no matter how you do it, you must return the response object, the very last line. You can find all the methods of the Response class here in the source code.

If you wanted to render a view which requires variables to be sent you do it like this and capture the output of renderView().


 $content = $this->renderView('blog/display_article.html.twig', [
            'title' => $title,
            'article' => $article,
            'tags' => implode(', ', $tags),
            'tagLinks' => $links,
            'edit' => $editLink,
            'affiliateUrl' => $affiliateUrl,
            'backButton' => $backButton
        ]);

Note : do not just use php setcookie or setrawcookie. The reason is they start sending output headers to the browser, which may interfere with how symfony works. You probably won’t notice in a browser, but you may get errors when testing your controllers with functional tests etc.

Personally I created a huge class which extends DateTime which has all kinds of methods for adding days, hours, removing them and doing other math. I’d share it on github but it has bugs since I wrote it way back in version 5 of php in 2012. Some changes were made to DateTime and I haven’t had time to review them all and hunt down the changes that need to be made yet. I’ll probably do it and add it to github eventually.  But for now I use time() + seconds. It’s not the best solution but it works and I only need this one cookie.

And another person found me more hidden docs about cookies, I wish I had this days ago.

As another note. Any values you put in a cookie you must sanitize before trying to use them in any way since users can access and change regular cookie values.

Categories
Resources Web Development

Symfony 5 how to clear the cache

I can never ever remember where I see anything ever I read entirely too much about entirely too many subjects. I mostly use this site as my own personal google.

To clear all caches
php bin/console cache:pool:clear cache.global_clearer

Symfony docs link to more info.

Categories
Resources Web Development

Doctrine migrations links

I have trouble navigating the Symfony documentation so I create lists of links here so I can just easily find what I need again later.

MigrationsBundle link and info -> shows how to run migrations and all of the doctrine migration related commands

Doctrine Symfony overview -> more information about doctrine and migrations.

Categories
Uncategorized

It’s in my head it must be in yours. Why all documentation sucks ass!!!

Most documentation sucks. It seems most authors have this idiotic idea that whatever is in their head, all their knowledge etc. magically transfers to the user as they read it. These are the same kind of idiots you meet in society that drone on about shit they understand to the totally uninitiated like they are talking to a pro. These are the kind of idiots that while talking don’t even have the ability to notice their true live audiences eyes glazing over and heads shaking in confusion. Nope these idiots are comfortable being idiots and we have to suffer. I don’t get it.

Ever been reading along documentation and the author just keeps throwing out concepts/terms left and right without even giving a link, leaving you in the dark like WTF? Isn’t it fun?

Here is a tip to anyone who writes documentation. No matter how stupid simple a concept seems to you, there will be people, many people who have never come across the concept. Instead of being a snobby dickhead with an attitude like “Well you have to have a minimum idea of how blah blah blah works because I do”. You should provide information, links etc. to any concept that anyone may not have encountered.  It is not that damn hard and doesn’t take much time and it makes you look much smarter and much less conceited and narcissistic.

This is the exact reason I started this website. So much of the documentation for everything sucks ass. Most documentation I come across these days leaves me saying WTF. Look at openssl or openssh or example so many WTF’s in those docs.

And what is worse is the assholes who write blogs who just copy other blog posts. So you get entire search pages full of shit articles all repeating the same wrong shit that doesn’t work. Then that shit information ends up on stackxxxx.com sites from idiot kids who copy and paste shit for points and don’t care if the answer is right, they just want popular points. I often just avoid stack sites these days. Those sites are only handy if you are doing the most basic simple shit. Once you go into complex issues those sites are wrong answer land 100% of the time.