Today, I woke up and thought, “Hmmm, I’ll do some work on my website.”
Then, it came to me, and I decided it was interesting, so I made it.
This is what I dreamt up and produced today, here for you to look at, download, use it, admire it, shower me with praise, or… preferably…
What does it do? You tell me! Maybe it’s useful or something, I don’t know.
I just like it because I thought it up and then wrote every line of it myself.
Here are the files. There’s a zip file at the end for you to download too if you’d like.
Total lines of code (lines in file) - 398 (I think that’s pretty good considering what it can do!)
Asherwolfstein\Semantic\Function
AsherWolfstein/Semantic/Function/Interface/Resolver.php
<?php
// Author : Asher Wolfstein asherwolfstein.com
// License : MIT
// Copyright 2026 Asher Wolfstein / Holley
declare ( strict_types = 1 ) ;
namespace AsherWolfstein\Semantic\Function\Interface ;
use AsherWolfstein\Semantic\Function;
interface Resolver {
/**
* @param Function\ArgumentBag $arguments
* @param Function\ParameterMap $parameter_map
* @param mixed $result
* @param Function\LoopControl $loop
*
* @return void
*/
function resolve (
Function\ArgumentBag $arguments ,
Function\ParameterMap $parameter_map ,
mixed & $result ,
Function\LoopControl $loop
) : void ;
}AsherWolfstein/Semantic/Function/ArgumentBag.php
<?php
// Author : Asher Wolfstein asherwolfstein.com
// License : MIT
// Copyright 2026 Asher Wolfstein / Holley
declare ( strict_types = 1 ) ;
namespace AsherWolfstein\Semantic\Function\Interface ;
use AsherWolfstein\Semantic\Function;
interface Resolver {
/**
* @param Function\ArgumentBag $arguments
* @param Function\ParameterMap $parameter_map
* @param mixed $result
* @param Function\LoopControl $loop
*
* @return void
*/
function resolve (
Function\ArgumentBag $arguments ,
Function\ParameterMap $parameter_map ,
mixed & $result ,
Function\LoopControl $loop
) : void ;
}AsherWolfstein/Semantic/Function/Concatenator.php
<?php
// Author : Asher Wolfstein asher@wolfste.in
// License : MIT
// Copyright 2026 Asher Wolfstein / Holley
declare ( strict_types = 1 ) ;
namespace AsherWolfstein\Semantic\Function;
class Concatenator
implements Interface\Resolver {
public const string SEPARATOR = 'separator' ;
public const string DEFAULT_SEPARATOR = '' ;
/**
* @param string $default_separator
*/
public function __construct (
protected string $default_separator = self::DEFAULT_SEPARATOR
) {}
/**
* @param ArgumentBag $arguments
* @param ParameterMap $parameter_map
* @param mixed $result
* @param LoopControl $loop
*
* @return void
*/
public function resolve (
ArgumentBag $arguments ,
ParameterMap $parameter_map ,
mixed & $result ,
LoopControl $loop
) : void {
$separator = $arguments -> has ( self::SEPARATOR )
? ( string ) $arguments -> get ( self::SEPARATOR )
: $this -> default_separator ;
$parameters = [];
foreach ( $parameter_map -> parameters () as $parameter ) {
if ( $arguments -> has ( $parameter ) ) $parameters [] = $parameter;
}
$parameter_index = 0;
$num_parameters = \count ( $parameters ) ;
foreach ( $parameters as $parameter ) {
$parameter_index ++ ;
$value = $arguments -> get ( $parameter ) ;
$result .= ( $resolver = $parameter_map -> get ( $parameter )
?-> resolve
)
? $resolver ( $value )
: ( ( $parameter_index == $num_parameters )
? $value
: "$value$separator"
);
}
$loop -> stop () ;
}
}AsherWolfstein/Semantic/Function/LoopControl.php
<?php
// Author : Asher Wolfstein asher@wolfste.in
// License : MIT
// Copyright 2026 Asher Wolfstein / Holley
declare ( strict_types = 1 ) ;
namespace AsherWolfstein\Semantic\Function;
class LoopControl {
/**
* @var bool
*/
protected bool $loop = false;
/**
* @return void
*/
public function loop () : void { $this -> loop = true ; }
/**
* @return bool
*/
public function should () : bool { return $this -> loop ; }
/**
* @return void
*/
public function stop () : void { $this -> loop = false ; }
}AsherWolfstein/Semantic/Function/ParameterMap.php
<?php
// Author : Asher Wolfstein asher@wolfste.in
// License : MIT
// Copyright 2026 Asher Wolfstein / Holley
declare ( strict_types = 1 ) ;
namespace AsherWolfstein\Semantic\Function;
class ParameterMap {
/** @param array<int|string, ParameterSpec> $specifications */
public function __construct(
/** @var array<int|string, ParameterSpec> */
protected array $specifications
) {}
/**
* @param string $name
*
* @return ParameterSpec|null
*/
public function __get ( string $name ) : ? ParameterSpec
{ return $this -> get ( $name ) ; }
/**
* @param array $specification_map
*
* @return static
*/
public static function from_array ( array $specification_map ) : self {
return new static ( \array_map (
fn ( $specification )
=> $specification instanceof ParameterSpec
? $specification
: ParameterSpec ::from_array ( $specification ) ,
$specification_map
) ) ;
}
/**
* @param array<int|string, mixed> $arguments
*
* @return array<int|string, mixed>
*
* @throws \InvalidArgumentException
*/
public static function order_parameters ( array $arguments ) : array {
$positional = [];
$named = [];
foreach ( $arguments as $name => $value ) {
if ( \is_string ( $name ) && \ctype_digit ( $name ) )
throw new \InvalidArgumentException ( "! numeric string key ( $name )" ) ;
if ( \is_int ( $name ) ) $positional [ $name ] = $value;
else $named [ $name ] = $value;
}
if ( $positional ) \ksort ( $positional );
if ( $positional || $named ) $arguments = $positional + $named;
return $arguments;
}
/**
* @param array<int|string, mixed> $arguments
*
* @return void
*
* @throws \InvalidArgumentException
*/
public function check ( array $arguments ) : void {
foreach ( $arguments as $name => $value ) {
$specification = $this -> get ( $name ) ;
if ( $specification
&& ( $check = $specification -> check )
&& ! $check ( $value )
)
throw new \InvalidArgumentException ( "! check ( $name )" ) ;
}
}
/**
* @param int|string $name
*
* @return ParameterSpec|null
*/
public function get ( int|string $name ) : ? ParameterSpec
{ return $this -> specifications [ $name ] ?? null ; }
/** @return array<int|string> */
public function parameters () : array { return \array_keys ( $this -> specifications ) ; }
}AsherWolfstein/Semantic/Function/ParameterSpec.php
<?php
// Author : Asher Wolfstein asher@wolfste.in
// License : MIT
// Copyright 2026 Asher Wolfstein / Holley
declare ( strict_types = 1 ) ;
namespace AsherWolfstein\Semantic\Function ;
class ParameterSpec {
public const string CHECK = 'check' ;
public const string RESOLVE = 'resolve' ;
/** @var callable|null */
public $check ;
/** @var callable|null */
public $resolve ;
/**
* @param callable|null $check
* @param callable|null $resolve
*/
public function __construct(
? callable $check = null ,
? callable $resolve = null
) {
$this->check = $check;
$this->resolve = $resolve;
}
/**
* @param array $specification
*
* @return static
*/
public static function from_array ( array $specification ) : self {
return new static (
$specification [ self::CHECK ] ?? null ,
$specification [ self::RESOLVE ] ?? null
) ;
}
}AsherWolfstein/Semantic/Function/Semantic.php
<?php
// Author : Asher Wolfstein asher@wolfste.in
// License : MIT
// Copyright 2026 Asher Wolfstein / Holley
declare ( strict_types = 1 ) ;
namespace AsherWolfstein\Semantic\Function;
class Semantic {
/**
* @param Interface\Resolver $resolver
* @param ParameterMap $parameter_map
* @param int $maximum_iterations
*
* @throws \InvalidArgumentException
*/
public function __construct (
protected Interface\Resolver $resolver ,
protected ParameterMap $parameter_map ,
protected int $maximum_iterations = 1
) {
if ( $maximum_iterations < 1 )
throw new \InvalidArgumentException ( "! max_iterations >= 1 ( {$this -> maximum_iterations} )" ) ;
}
/**
* @param mixed ...$arguments
*
* @returns mixed
*
* @throws \InvalidArgumentException
* @throws \RuntimeException
*/
public function run (
... $arguments
) : mixed {
// throws \InvalidArgumentException
$this -> parameter_map
-> check ( $arguments = ParameterMap ::order_parameters ( $arguments ) ) ;
$argument_bag = new ArgumentBag ( $arguments ) ;
$loop = new LoopControl () ;
$result = null;
for (
$iteration = 0 ;
$iteration < $this->maximum_iterations ;
$iteration ++
) {
$loop -> stop();
$this -> resolver
-> resolve (
$argument_bag ,
$this -> parameter_map ,
$result ,
$loop
) ;
if ( ! $loop -> should () ) return $result;
}
throw new \RuntimeException ( "iteration > max_iterations ( $iteration > {$this -> maximum_iterations} )" ) ;
}
}semantic.zip
Anyways.
An Example
Here’s an example of how one might use it:
<?php
// Copyright 2026 Asher Wolfstein / Holley
// Author : Asher Wolfstein asherwolfstein.com
// License : MIT
declare ( strict_types = 1 ) ;
use AsherWolfstein\Semantic\Function;
class Attribute {
public static function format (
... $arguments
) : string {
$esc_url
= \is_callable ( $arguments [ 'esc_url' ] ?? null )
? $arguments [ 'esc_url' ]
: esc_url ( ... ) ;
$parameter_map
= Function\ParameterMap ::from_array(
[ 'action' => [
'check' => is_string ( ... ) ,
'resolve' =>
fn ( string $action )
=> 'action="' . $esc_url ( $action ) . '"' ,
] ,
'method' => [
'check' =>
fn ( $method )
=> is_string ( $method )
&& match ( $method ) { 'post', 'get' => $method , default => false } ,
'resolve' => fn ( string $method )
=> "method=\"$method\"" ,
] ,
'target' => [
'check' => is_string ( ... ) ,
'resolve' =>
fn ( string $target )
=> 'target="' . esc_attr ( $target ) . '"' ,
] ,
]
);
return new Function\Semantic (
new Function\Concatenator ( ' ' ) ,
$parameter_map
) -> run ( ... $arguments ) ;
}
}
Attribute ::format (
action : 'https://example.com/form-input' ,
method : 'post' ,
target : 'my-form'
);Any thoughts, questions, critiques, leave ’em below!
