Skip to content

Instantly share code, notes, and snippets.

@tiagofrancafernandes
Last active December 7, 2023 03:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tiagofrancafernandes/3d37a4b72700d4f1f70d9291d212f4ff to your computer and use it in GitHub Desktop.
Save tiagofrancafernandes/3d37a4b72700d4f1f70d9291d212f4ff to your computer and use it in GitHub Desktop.
dev-PHP Custom Classes
use TiagoF2\F2ArrayCache\F2ArrayCache;

if (!$cache->isValid('abc')) {
    echo 'Invalid. Do some here...' . PHP_EOL;
}

var_export([
    'abc' => $cache->get('abc', 'PADRAO'),
    'put_abc' => $cache->put('abc', 50, 'valor enviado primeiro'),
    'get_abc' => $cache->get('abc', 'PADRAO'),
]);

var_export([
    'abc' => $cache->get('abc', 'PADRAO'),
    'remember_abc' => $cache->remember('abc', 55, fn () => 'valor enviado em ' . date('d/m H:i:s')),
    'get_abc' => $cache->get('abc', 'PADRAO'),
]);

if (!$cache->isValid('abc')) {
    // Never will called
    echo 'Invalid. Do some here...' . PHP_EOL;
}
<?php
namespace TiagoF2\F2ArrayCache;
use DateTime;
use Closure;
use Exception;
class F2ArrayCache
{
public const NO_DATA_VALUE = ':::NODATA:::';
protected array $data = [];
public function __construct(
protected ?string $cacheStorePath = null,
) {
$this->loadCacheStore();
}
public static function make(?string $cacheStorePath = null)
{
return new static($cacheStorePath);
}
protected function updated()
{
$this->persistCacheStore();
}
protected function wipe()
{
$this->data = [];
$this->persistCacheStore();
}
protected function patrol()
{
foreach ($this->data as $cacheKey => $cacheData) {
$this->get($cacheKey);
}
}
protected function loadCacheStore(): void
{
if (!$this->cacheStorePath) {
return;
}
if (file_exists($this->cacheStorePath) && !is_file($this->cacheStorePath)) {
throw new Exception("{$this->cacheStorePath} is not a valid JSON file.", 1);
}
if (!file_exists($this->cacheStorePath)) {
return;
}
$content = file_get_contents($this->cacheStorePath);
$data = json_decode($content, true);
$this->data = $data && is_array($data) ? $data : [];
$this->patrol();
}
protected function persistCacheStore(): void
{
if (!$this->cacheStorePath) {
return;
}
if (file_exists($this->cacheStorePath) && !is_file($this->cacheStorePath)) {
throw new Exception("{$this->cacheStorePath} is not a valid JSON file.", 1);
}
file_put_contents(
$this->cacheStorePath,
json_encode($this->data, 128 | 64),
);
}
protected function forgetOnData(
string $key,
): void {
unset($this->data[$key], $this->data[trim($key)]);
$this->updated();
}
protected function putOnData(
string $key,
mixed $value,
null|string|DateTime $expiresIn,
): ?bool {
$key = trim($key);
if (empty($key)) {
return false;
}
$expiresIn = is_object($expiresIn) && is_a($expiresIn, DateTime::class)
? $expiresIn : (
$expiresIn
? new DateTime($expiresIn)
: (new DateTime())->modify('-3 days')
);
$now = $this->getNow();
if ($now >= $expiresIn) {
$this->forgetOnData($key);
return false;
}
$this->data[$key] = [
'timestamp' => serialize($now),
'expiresIn' => serialize($expiresIn),
'value' => $value,
];
$this->updated();
return true;
}
protected function getFromData(
string $key,
): mixed {
$key = trim($key);
if (empty($key)) {
return static::NO_DATA_VALUE;
}
$dataValue = $this->data[$key] ?? static::NO_DATA_VALUE;
if ($dataValue === static::NO_DATA_VALUE) {
return static::NO_DATA_VALUE;
}
if (!is_array($dataValue)) {
$this->forgetOnData($key);
return static::NO_DATA_VALUE;
}
$expiresIn = static::unserializeDate($dataValue['expiresIn'] ?? null);
$now = $this->getNow();
$expiresIn = $expiresIn ?: (new DateTime())->modify('-3 days');
if ($now >= $expiresIn) {
$this->forgetOnData($key);
return static::NO_DATA_VALUE;
}
return $dataValue['value'] ?? static::NO_DATA_VALUE;
}
public static function unserializeDate(mixed $date): ?DateTime
{
try {
if (!is_string($date) || strlen($date) < 80) {
return null;
}
$valid = str_contains($date, '"DateTime"')
&& str_contains($date, '"timezone"')
&& str_contains($date, ';s:');
if (!$valid) {
return null;
}
$date = unserialize($date);
return is_object($date) && is_a($date, DateTime::class) ? $date : null;
} catch (\Throwable $th) {
return null;
}
}
public function getNow(): DateTime
{
return new DateTime();
}
public function put(
string $key,
int $ttl,
mixed $value,
): ?bool {
$expiresIn = (new DateTime())->modify(intval($ttl) . ' seconds');
$value = is_object($value) && is_a($value, Closure::class) ? $value() : $value;
return $this->putOnData(
$key,
$value,
$expiresIn,
);
}
public function get(
string $key,
mixed $defaultValue = null,
): mixed {
$value = $this->getFromData($key);
if ($value === static::NO_DATA_VALUE) {
$this->forgetOnData($key);
return $defaultValue;
}
return $value ?? $defaultValue;
}
public function remember(
string $key,
int $ttl,
Closure $closure
): mixed {
if (!$ttl || $ttl <= 0) {
$this->forgetOnData($key);
return null;
}
$value = $this->getFromData($key);
if ($value === static::NO_DATA_VALUE) {
$this->forgetOnData($key);
$value = $closure();
$this->put($key, $ttl, $value);
}
return $value;
}
public function isValid(
string $key
): bool {
return $this->exists($key);
}
public function exists(
string $key
): bool {
$value = $this->getFromData($key);
return ($value !== static::NO_DATA_VALUE);
}
public static function onTime(
null|string|int $ttl,
null|string|DateTime $startDate = null,
null|string|DateTime $endDate = null,
): bool {
if (empty($ttl)) {
return false;
}
if (!$startDate) {
return false;
}
$startDate = is_object($startDate) && is_a($startDate, DateTime::class) ? $startDate : new DateTime($startDate ?: null);
$endDate = is_object($endDate) && is_a($endDate, DateTime::class) ? $endDate : new DateTime($endDate ?: null);
$diff = $startDate->diff($endDate);
$diffSeconds = $diff->days * 24 * 60;
$diffSeconds += $diff->h * 60;
$diffSeconds += $diff->s;
return ($diffSeconds < $ttl);
}
}
class PlainTextToPDF
{
    protected ?string $template = null;
    protected ?array $replaces = null;
    protected ?string $encoding = null;
    protected ?string $font = null;
    protected array $lines = [];
    public const FONTS = [
        'courier'=>'Courier',
        'courierB'=>'Courier-Bold',
        'courierI'=>'Courier-Oblique',
        'courierBI'=>'Courier-BoldOblique',
        'helvetica'=>'Helvetica',
        'helveticaB'=>'Helvetica-Bold',
        'helveticaI'=>'Helvetica-Oblique',
        'helveticaBI'=>'Helvetica-BoldOblique',
        'times'=>'Times-Roman',
        'timesB'=>'Times-Bold',
        'timesI'=>'Times-Italic',
        'timesBI'=>'Times-BoldItalic',
    ];

    public function __construct(
        ?string $template = null,
        ?array $replaces = null,
        ?string $encoding = null,
        ?string $font = null,
    ) {
        $this->template = $template;
        $this->replaces = $replaces;
        $this->encoding = $encoding;
        $this->font = $font;
    }

    public static function getFontNameById(?string $fontId = null): ?string
    {
        $fontId = trim("{$fontId}") ?: 'courier';
        return static::FONTS[$fontId] ?? null;
    }

    public function getTemplate(): string
    {
        return $this->template ?? $this->getDefaultTemplate();
    }

    public function getReplaces(): ?array
    {
        return $this->replaces;
    }

    public function getEncoding(): ?string
    {
        return $this->encoding;
    }

    public function getFont(): string
    {
        return static::getFontNameById($this->font) ?? 'Courier';
    }

    public static function getDefaultTemplate(): string
    {
        $template = <<<'TXT'
        %PDF-1.1
        1 0 obj
        <<
        /CreationDate (D:###CREATION_DATE###)
        /ModDate (D:###MOD_DATE###)
        /Creator (PlainTextToPDF v23.12.06 (F2 Sistemas, 2023))
        /Producer (PlainTextToPDF v23.12.06 (F2 Sistemas, 2023))
        /Author (F2 Sistemas)
        /Subj (###SUBJECT###)
        /Subject (###SUBJECT###)
        /Title (###TITLE###)
        /Keywords (###KEYWORDS###)
        >>
        endobj
        2 0 obj
        << /Type /Catalog /Pages 3 0 R >>
        endobj
        4 0 obj
        << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /###FONT### /Encoding /WinAnsiEncoding >>
        endobj
        5 0 obj
        << /Font << /F1 4 0 R >> /ProcSet [ /PDF /Text ] >>
        endobj
        6 0 obj
        << /Type /Page /Parent 3 0 R /Resources 5 0 R /Contents 7 0 R >>
        endobj
        7 0 obj
        << /Length 8 0 R >>
        stream
        BT
        /F1 10 Tf
        1 0 0 1 50 786 Tm
        9 TL
        ###CONTENT###
        ET
        BT
        endstream
        endobj
        8 0 obj
        381
        endobj
        3 0 obj
        << /Type /Pages /Count 1 /MediaBox [ 0 0 612 792 ] /Kids [ 6 0 R ] >>
        endobj
        xref
        0 9
        0000000000 65535 f
        0000000009 00000 n
        0000000132 00000 n
        0000000862 00000 n
        0000000181 00000 n
        0000000259 00000 n
        0000000330 00000 n
        0000000410 00000 n
        0000000843 00000 n
        trailer
        << /Size 9 /Root 2 0 R /Info 1 0 R >>
        startxref
        947
        %%EOF
        TXT;

        return $template;
    }

    public function addLine(?string $content = null): static
    {
        $this->lines[] = ($content ?? PHP_EOL);

        return $this;
    }

    public function setLine(int|string $key, ?string $content = null): static
    {
        $this->lines[$key] = ($content ?? PHP_EOL);

        return $this;
    }

    public function getLineCount(): int
    {
        return count($this->lines);
    }

    public function getLines(): string
    {
        $content = '';

        foreach ($this->lines as $lineKey => $lineContent) {
            $content .= "({$lineContent})'\n";
        }

        return $content;
    }

    public function getPdfContent(
        ?string $title = null,
        ?string $encoding = null,
        array $replaces = [],
    ): string {
        $template = static::getTemplate();
        $encoding = $encoding ?? static::getEncoding();

        $replaces = array_merge($this->getReplaces() ?? [], $replaces);

        $fontKey = trim($replaces['font'] ?? '');
        $fontName = static::getFontNameById($fontKey) ?: $fontKey;
        $replaces = array_merge(
            $replaces,
            [
                '###TITLE###' => $title ?? $replaces['title'] ?? 'Text to PDF by F2 Sistemas',
                '###CONTENT###' => $replaces['content'] ?? $this->getLines(),
                '###CREATION_DATE###' => date('YmdHis'),
                '###MOD_DATE###' => date('YmdHis'),
                '###SUBJECT###' => $replaces['subject'] ?? 'PlainTextToPDF',
                '###KEYWORDS###' => $replaces['keywords'] ?? 'PlainTextToPDF',
                '###FONT###' => $fontName ?: $this->getFont(),
            ]
        );

        $replaces = array_filter(
            $replaces,
            fn($value, $key) => (is_string($value) && is_string($key)),
            ARRAY_FILTER_USE_BOTH
        );

        $result = str_replace(
            array_keys($replaces),
            array_values($replaces),
            $template
        );

        return trim("{$encoding}") ? mb_convert_encoding($result, $encoding) : $result;
    }

    public function storeAs(
        string $destinationPath,
        ?string $title = null,
        ?string $encoding = null,
        array $replaces = [],
    ): bool|int {
        $content = $this->getPdfContent(
            $title,
            $encoding,
            $replaces,
        );

        return file_put_contents($destinationPath, $content);
    }
}

Usage:

require __DIR__ . '/PlainTextToPDF.php';

// Usage:
$pdf = new \App\Helpers\PlainTextToPDF(
    encoding: 'ISO-8859-1',
);
$pdf->addLine('depósito bancário');
$pdf->addLine('comunhão');
$pdf->addLine('caminhão ü');
$pdf->addLine(date('c'));
// echo $pdf->getLines(); // Total de linhas até o momento
// echo $pdf->getPdfContent(); // Conteúdo do PDF

$pdf->storeAs('my-file.pdf');

$pdf->storeAs('my-file-font-helvetica.pdf', replaces: [
    'font' => 'helvetica',
]);

/*
// Gerando vários arquivos com várias fontes disponíveis
$fonts = [
    'courier',
    'courierB',
    'courierI',
    'courierBI',
    'helvetica',
    'helveticaB',
    'helveticaI',
    'helveticaBI',
    'times',
    'timesB',
    'timesI',
    'timesBI',
];

foreach ($fonts as $fontKey) {
    $pdf->storeAs("my-file-font-{$fontKey}.pdf", replaces: [
        'font' => $fontKey,
    ]);
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment