<?php
 abstract class vB_BbCode implements vB_Interface_BBCode { use vB_Trait_NoSerialize; public const TYPE = 'BbCode'; protected vB_BbCodeHelper $bbcodeHelper; protected $record = [ 'id' => null, 'nodeid' => null, 'bbcode_type' => '', 'hash' => '', 'data' => [], 'expires' => 0, ]; protected $writable = [ 'nodeid' => true, 'data' => true, 'expires' => true, ]; public const USES_DATA_STORAGE = false; protected bool $modified = false; final public function getInsertData() { $record = $this->record; $record['data'] = json_encode($record['data']); $record['bbcode_type'] = static::getType(); return $record; } protected function __construct(vB_BbCodeHelper $bbCodeHelper, $record, $modified = false) { $this->bbcodeHelper = $bbCodeHelper; $this->setRecord($record, $modified); } private function setRecord(array $record, bool $modified = false) : void { $this->record = array_intersect_key($record, $this->record); $this->modified = $modified; if (static::USES_DATA_STORAGE) { $expired = $this->isDataExpired(); if (empty($this->record['data']) OR $expired) { $this->updateDataAndexpires(); } } } public function setRenderOptionsAndContext(array $renderOptions, array $renderContext) : void { } protected $usercontext; final public function setUserContext(?vB_UserContext $usercontext) : void { $this->usercontext = $usercontext; } protected function isDataExpired() : bool { $now = time(); $expired = ( !empty($this->record['expires']) AND $now >= $this->record['expires'] ); return $expired; } protected static function getDefaultRecord() : array { return [ 'id' => null, 'nodeid' => null, 'bbcode_type' => static::getType(), 'hash' => '', 'data' => [], 'expires' => 0, ]; } abstract public static function generateFreshFromDataAndOption(vB_BbCodeHelper $bbCodeHelper, string $data, ?string $option) : static; public static function restoreFromDBRecord(vB_BbCodeHelper $bbcodeHelper, $dbrecord) : static { $record = static::getDefaultRecord(); $record = array_intersect_key($dbrecord, $record); $record['data'] = json_decode($dbrecord['data'], true); $modified = false; return new static($bbcodeHelper, $record, $modified); } final public static function determineClassname($type) : ?string { if (class_exists($type) AND is_subclass_of($type, 'vB_BbCode')) { return $type::getCanonicalClassname(); } $class = 'vB_BbCode_' . ltrim($type, '_'); if (!class_exists($class) OR !is_subclass_of($class, 'vB_BbCode')) { return null; } return $class::getCanonicalClassname(); } final public static function getCanonicalClassname() { return get_called_class(); } final public static function getType() : string { return ucfirst(substr(get_called_class(), 10)); } public function __isset(string $name) : bool { return isset($this->record[$name]); } public function __get(string $name) : mixed { if (array_key_exists($name, $this->record)) { return $this->record[$name]; } else if ($name == 'modified') { return $this->modified; } } public function __set(string $name, $value) : void { if (array_key_exists($name, $this->record) AND array_key_exists($name, $this->writable) AND $value !== $this->record[$name]) { $this->record[$name] = $value; $this->modified = true; } } protected function refreshExpiredDataAtConstruction() : void { return; } protected function updateDataAndexpires() : void { $this->refreshExpiredDataAtConstruction(); return; } abstract public static function getHash(vB_BbCodeHelper $bbCodeHelper, string $data, ?string $option) : string; abstract public function canHandleBbCode(string $data, ?string $option) : bool; protected $rendered = false; abstract public function renderBbCode(string $data, ?string $option) : string; public function getRenderMetaData() : array { return []; } } 