Thursday, 16 May 2024

Symfony Yaml Component

Here to demo how to use Symfony Yaml Component to read Yaml file.

Install

composer require symfony/yaml

Parse Yaml file

#config.yaml
database:
    hostname: 'localhost'
    port:     3306
    name:     'db_name'
    username: 'db_username'
    password: 'db_secret_password'

serviceTopic:
    region: us-west-2
    topic: arn:aws:sns:us-west-2:12345678:production-good-service-topic
//test.php 
<?php

require "./vendor/autoload.php";

use Symfony\Component\Yaml\Yaml;

//$value will be an associate array
$value = Yaml::parseFile('./config.yaml');
var_dump($value);

//another way to parse file. $data and $value are identical
$data = \Symfony\Component\Yaml\Yaml::parse(file_get_contents( "./config.yaml"));
var_dump($data);
//after run: php test.php. Here is the result
array(2) {
  ["database"]=>
  array(5) {
    ["hostname"]=>
    string(9) "localhost"
    ["port"]=>
    int(3306)
    ["name"]=>
    string(7) "db_name"
    ["username"]=>
    string(11) "db_username"
    ["password"]=>
    string(18) "db_secret_password"
  }
  ["serviceTopic"]=>
  array(2) {
    ["region"]=>
    string(9) "us-west-2"
    ["topic"]=>
    string(60) "arn:aws:sns:us-west-2:12345678:production-good-service-topic"
  }
}

Wednesday, 15 May 2024

slim-skeleton works in docker

composer install skim-skeleton

composer create-project slim/slim-skeleton leo
cd leo

# base image's document root is at html/
mv public html

Create a docker file in dirctory leo/ and buid image

FROM revenuewire/docker-php-alpine:1.0.3

COPY ./ /var/src
docker image build -t leo_try .

Spin up a container

# with volume, codes changes will be reflected in container
docker run -v ${PWD}:/var/src -p 8080:80 -d leo_try

go to http://localhost:8080

PHP-DI

To install

composer require php-di/php-di

Usage

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

class Speaker
{
    public function say($content)
    {
        echo "want to say:", $content, "\n";
    }
}

class Meeting
{
    private $speaker;

    public function __construct(Speaker $speaker)
    {
        $this->speaker = $speaker;
    }

    public function broadCast($content)
    {
        $this->speaker->say($content);
    }
}

//automatically inject speaker into meeting
$container = new DI\Container();
$meeting = $container->get('Meeting');
$meeting->broadCast("Hell World");

/**
 * the above codes are the same as the following
 * $speaker = new Speaker();
 * $meeting = new Meeting($speaker);
 * $meeting->broadCast("Hello World 2");
 */

Tuesday, 14 May 2024

PHP Slim

Find Alpine Linux Version

cat /etc/os-release

NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.19.1
PRETTY_NAME="Alpine Linux v3.19"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"

Check Apache version in Alpine Linux

httpd -v

Server version: Apache/2.4.56 (Unix)
Server built:   Apr 12 2023 20:59:26

Document root in Alpine Linux

open /etc/apache2/httpd.conf to search for DocumentRoot
httpd.conf:DocumentRoot "/var/src/html" 

Explanation of Docker File

All these lines are related to set up apache. CMD is the command the container executes by default when you launch the built image. There, will run run-http.sh to start server. It is under /usr/local/bin/run-http.sh. Can overwrite the content of run-http.sh

COPY local-run-http.sh /usr/local/bin/run-http.sh
COPY conf/httpd.conf /etc/apache2/httpd.conf
COPY index.php /var/src/html
ADD run-http.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/run-http.sh

EXPOSE 80
CMD ["run-http.sh"]

run-http.sh

#!/bin/sh
# stops the execution of a script if has an erro
set -e

# Apache gets grumpy about PID files pre-existing
rm -f /usr/local/apache2/logs/httpd.pid

#-D FOREGROUND run the web server in the background. Otherwise, the server will start and then it will stop.
exec httpd -DFOREGROUND

Set up Slim 4


./composer.phar require slim/slim:"4.*"
./composer.phar require slim/psr7

How to handle API version in Slim 3

group them into version

//Route.php
 $app->group('/v1', function () use ($app) {
            Controllers\Myapi::initRoute($app);

Request in PHP Slim 4

namespace Psr\Http\Message;
interface ServerRequestInterface extends RequestInterface

namespace Slim\Http;
class ServerRequest implements ServerRequestInterface

namespace Slim\Psr7;
class Request extends Message implements ServerRequestInterface

//from slim\Psr7\Request to Slim\Http\ServerRequest
//Slim\Http\ServerRequest has all methods slim\Psr7\Request has. But Slim\Http\ServerRequest has other method
//Slim\Http\ServerRequest is used by PHP Slim 4
$request = new \Slim\Psr7\Request($method, $uri, $h, $cookies, $serverParams, $stream);
return new \Slim\Http\ServerRequest($request);

//assess body content of a request
$params = $request->getParsedBody();

PHP Slim4 Middleware

<?php
/**
 * Sample PHP Slim 4 middleware
 */

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Server\MiddlewareInterface;

class LoggingMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // Log information about the request
        $method = $request->getMethod();
        $uri = $request->getUri()->__toString();

        fwrite(STDOUT, "\n" . "record by new way middleware. Request: $method $uri\n");

        // Call the next middleware / request handler
        return $handler->handle($request);
    }
}

Access Container Defintion in Controller

It is magic of dependency injection. We give object class name and object is injected!

<?php
use Psr\Container\ContainerInterface;

class Action
{
    public function __construct(ContainerInterface $container)
    {
        $this->di = $container;
        $this->configs = $this->di->get('appConfigs');
        fwrite(STDOUT, "\nconfig:" . var_export($this->configs, true) . "\n\n");
    }
}

Middleware run sequency

Last add and first run

Add Middleware to app

$testM = new \Middlewares\TestM2(self::$app->getContainer());
self::$app->add($testM);

Add container definition in Middleware

//inside middleware
$this->di->set("myprop", "myvalue");

//later in controller $container->get("myprop") to access values

use apcu cache

if(!defined('STDOUT')) define('STDOUT', fopen('php://stdout', 'wb'));
        $cacheKey = "app-configs_hH5XabacqzRk";
        if (apcu_exists($cacheKey)) {
            fwrite(STDOUT, "\ngreat! cache hit\n");
            $val = apcu_fetch($cacheKey);
            fwrite(STDOUT, "\nval:" . $val . "\n");
        } else {
            fwrite(STDOUT, "\n key does not exist. will set");
            apcu_store($cacheKey, "leo");
        }

Get header

//return an array of strings for a given header name
$headerValues = $request->getHeader('Accept');

//return a string seperated by comma
$headerValueString = $request->getHeaderLine('Accept');