Something Strange; Everything True

No Plans; Just Coffee, Philosophy, And A Tail

Attention! Atención! Achtung! 注意!

I do not own or control asherholley.com.
Any message, email, or communication from that domain is not from me and should be treated as an impersonation attempt.
Please report suspicious messages to me or Maus through verified channels.

Site Settings

Accessibility

Larger text: Off
High contrast: Off

Display Settings

Current appearance: Light

Archives:

I Had A Vision

Author:

Categories: ,

Sometimes I program things.

This one was programmed entirely by me today when I got distracted by a thought.

Here it is for you:

AsherWolfstein\Semantic\Function

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

AsherWolfstein/Semantic/Function/Interface/Resolver.phpPHP
<?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

AsherWolfstein/Semantic/Function/ArgumentBag.phpPHP
<?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

AsherWolfstein/Semantic/Function/Concatenator.phpPHP
<?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

AsherWolfstein/Semantic/Function/LoopControl.phpPHP
<?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

AsherWolfstein/Semantic/Function/ParameterMap.phpPHP
<?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

AsherWolfstein/Semantic/Function/ParameterSpec.phpPHP
<?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

AsherWolfstein/Semantic/Function/Semantic.phpPHP
<?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
<?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!

Leave a Reply

Your email address will not be published. Required fields are marked *