Search by

vosiz / va-tools

vosiz

PHP tools (from Variable Arts)

Package info

github.com/vosiz/va-tools

pkg:composer/vosiz/va-tools

Statistics

Installs: 72

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.10.0 2026-10-10 20:34 UTC

README

PHP tools to ease work

Motivation

A bag of small tools I kept rewriting in every PHP project - database access, URL parsing, XML/HTML output, flags, return values, debugging. Built on php-utils and php-enum. Made by personal need, so feel free to use it if it fits yours. MIT - use it however you like.

Requirements

  • PHP 7.4 or newer
  • Composer
  • vosiz/php-utils 1.12 and vosiz/php-enum 2.4 (installed by Composer)
  • PDO with a MySQL/MariaDB driver for the database part

Bug tracker

No tracked

Features

Everything checked in the Roadmap.

Roadmap

General

  • Signal (intensity class)
  • Retval (return value control, shorties, literals)
  • Shortcuts (shorties/aliases)

Database

  • Connection, config, CRUD
  • Connection info
  • Limit, order
  • Raw query, last insert id
  • Joins, group by, offset
  • Transactions
  • Parentheses for OR conditions

Parser

  • URL parser

Structure

  • Node hierarchy
  • Flags (flag system)
  • Credentials

Format

  • Simple XML builder
  • Simple HTML builder
  • Unset/Add/Remove for element attributes and classes
  • Simple JSON builder

Filter

  • Basic filter
  • String filtration

Debugger

  • Basic class with dumper (broswer dump + recursion detection)
  • Backtrace
  • More dumper variants

Tests

  • PHPUnit tests, tests.sh
  • Tests for the database part

Setup

composer require vosiz/va-tools

Used namespace: Vosiz\VaTools"particular-part"

F.e. to use Retval class (general/root):

use Vosiz\VaTools\Retval;

Other examples...

use Vosiz\VaTools\Structure\Credentials;
use Vosiz\VaTools\Db\DbConnectionConfig;
use Vosiz\VaTools\Db\DbConnection;
use Vosiz\VaTools\Format\XmlBuilder;

See "samples" folder to inspire.

Parser - URL parser

Parses URL to user defined structure

Include classes, something like this:

use Vosiz\VaTools\Parser\UrlStructure as UrlStruct;
use Vosiz\VaTools\Parser\UrlParser as Parser;

Then you need to define structure model you want to use, like this:

// asuming structure www.myurl.com/api/<controller>/<action>?par=value... or something similar
// somethng like: wwww.myurl.com/api/user/profile?userid=123...
// first parameter is the part of path to ignore (no host, only path), can be empty
$struct = UrlStruct::Create('api', ['controller', 'action']);

Pass it to a parser

$parser = new Parser($struct);                          // takes URI of current request
$parser = new Parser($struct, '/api/user/profile');     // or give it your own

Access parts you need

$parser->GetPartByKey('action');

Structure - Node hierarchy

Basically it is a node structure (tree, graph,...).

Lets setup this.

Include it to your project with desired method and use it f.e. like this:

use Vosiz\VaTools\Structure\NodeHierarchy as Nodeh;

Setup root node:

$root = Nodeh::Create("ROOT");

Add children nodes as you need:

$node_left1 = Nodeh::Create("node_L1", $root);
$node_left2 = Nodeh::Create("node_L2", $node_left1);
$node_right1 = Nodeh::Create("node_R1", $root);

Or set parents as you need:

$node_orphan = Nodeh::Create("node_no_papa");
$node_orphan->SetParent($root);

Add child or children explicetely (child(ren) setup will automatically set parent to them):

$node->AddChild($some_lonely_node);
$node->AddChildren($array_of_lonely_nodes);

Note

Node has one parent only. Give it a new one and it leaves the old one (root of the whole subtree follows). Node can not be placed under itself or its own descendant.

If needed to traverse node mesh (in-depth):

    $path = []; // here are names of nodes
    $node = $root;
    do {
        $path[] = $node->GetName();
        $node = $node->Next(); // step deeper or continue, NULL is end
    }
    while($node);

Structure - Flag system

It is a flag system suitable for configuration f.e. Instead of keeping distinct variables in class, you can you instance of this to keep it. It is more databse friendly as it works with integers, but for logic returns booleans.

Lets see how it works.

Include flagword to your project:

use Vosiz\VaTools\Structure\Flagword as Fword;

Or just the flag if you want to use it more traditionaly (enchanced bools basically):

use Vosiz\VaTools\Structure\Flag as Flag

Note

Flagword works with Flag

Flag has a simple interface, it works basically like boolean does, but it resolves "int vs bool" potencial issue, so you do not need to wory about having different logical values in configuration value.

class MyClassWithSimpleConfig {

    private $ConfigFlag;    // good old way
    private $NewConfigFlag; // Flag system

    public function OldConfig($value) {

        // check if int or bool
        ...

        $this->ConfigFlag = $processed_value;
    }

    public function NewConfig($value) {

        $this->NewConfigFlag = new Flag(); // flag is unset ( 0 / false)

    	// in case you want to set flag 
        $this->NewConfigFlag = new Flag(true);  // = 1

        // -- OR -- 
        $this->NewConfigFlag = new Flag();  // = 0
        $this->NewConfigFlag->Set();        // = 1
    }
}

Assuming you have getter for $NewConfigFlag field - you can manipulate with flag as you want.

$flag = $myclass->GetConfig(); // returns $NewConfigFlag (example)

// read flag value
$flag->IsSet(); // always bool

// set new value
$flag->Set($value); // default is 1

// unset flag
$flag->Unset(); // always 0

// toggle (switch)
$flag->Toggle(); // switching values

If you want to work with your variable as flag, simply convert it.

$b = false;
$i = 1;

$flag1 = Flag::ToFlag($b);
$flag2 = Flag::ToFlag($i);

Flagword is basically structure with Flags in it. Lets create one or two.

class MyClassWithMoreConfig {

    private $ConfigurationGeneral;
    private $ConfigurationSpecifics;
    ...

    public function __construct() {

        $this->ConfigurationGeneral = new Fword(2);     // I know i do not need more than 16 flags (2 bytes)
        $this->ConfigurationSpecifics = new Fword(5);   // I know i do not need more than 40 flags (5 bytes)
        ...
    }
}

Note

Flag system is designed to be lightweight - it detects your architecture (32/64) and rounds the size up to whole integers, so 2 bytes on 64bit system give you 64 flags.

Lets asume we have getters to configs.

$general = $myclass->GetConfig('general'); // or somthing like that
$specs = $myclass->GetConfig('specifics'); // or somthing like that

Now there are 2 ways to work with Flagword.

  1. As capsule for Flags
  2. Smart-capsule for Flags

In first case, the work is very similar to work with Flag(s). Lets setup some general configuration.

$general->SetFlag(4);           // config of index 4 is set (default)
$general->SetFlag(5, 1);        // config of index 5 is set
$general->SetFlag(8, 0);        // config of index 8 is unset
$general->SetFlag(9, $conf9);   // config of index 9 is set whatever value is stored in $conf9
...
// all others are set to 0 automatically

Now you can read them.

$flag8 = $general->ByIndex(8); // returns Flag of index 8
$gcfg8 = $flag8->IsSet();

Flagword which fits one integer (default size does) can be stored as a number and loaded back, f.e. to keep it in database.

$mask = $general->ToInt();              // bit n = flag n
$general = (new Fword())->FromInt($mask);

Second way is to use built-in functions. Lets use specifics and lets asume we have to define certain specifics to check later. I our little scenario lets asume we have defined constants of indeces.

$system = 0; // 0x00 - system flags in first 2 bytes
$admin = 16; // 0x10 - admin flags after second byte
...

$system_ready   = $system + 0;      // 0x00
$system_online  = $system + 5;      // 0x05
...

$admin_logged   = $admin + 1;       // 0x11
...

// want to name and register our mandatory flags
$specs->RegisterFlag($system_ready, 'System-ready');
$specs->RegisterFlag($admin_logged, 'Admin-loggd'); // typo
$specs->RegisterFlag($system_online, 'System-online');
...

// we later in system found typo (or whatever reason to rename it)
$specs->RegisterFlag($admin_logged, 'Admin-logged');

Now we can check if every mandatory flag was set in system if we want to. For example all mandatory checks were done, ready to fly boss.

if($specs->IsAllSet()) {

    // all set, move on...
    ...
}

Structure - Credentials

Simple login-like credential class. Stores username and password.

You can try to check if user is authorized like this:

$cr = new Credentials('user', '1234');
$authorized = $cr->Auth('user', '3215'); // do with the returned bool as you please

If you are afraid of password leak, via GetPassword() method, you can create password protected with placeholder.

$cr = new Credentials('user', '1234', true);
$pass = $cr->GetPassword();     // will end up with "*****" like something, does not have effect on authorization
$real = $cr->RevealPassword();  // real one, when you really need it (database connection uses this)

Data filtration - string

Simple filtration class which can be used to filter collection of data - in this case strings.

Lets assume we want to know if "MyNamespace" is correctly included (visible in list of defined classes list - something like "MyNamespace\MyClass").

Start with creating instance:

$data = classlist(); // in this example gets a class list
$filter = new StringFilter($data);

Tip

classlist is function defined in in Link package

This step is optional and depends on situation, but class list will surely contains something like "MyNamspace...\MyClass" or in this case lets assume "MyNamspace\MyClass" in short.

If we want to use full-string name, thats ok. Maybe we are having multipleclasses (like MyClass2 etc...). In this case we want to split each entry of list to reference full-string.

This is done simply by calling Split method.

$filter->Split("\\"); // in this case escaped "\"

Now we have 2 references to original data. "MyNamespace" => "MyNamspace\MyClass" "MyClass" => "MyNamspace\MyClass"

Now simply create a query with params and call filtration. Query is composed of command (string name of method or alias) and (if needed) parameters.

In our scenario we need to filter "our" classes, so lets filter it by "MyNamespace" to get all items from original list, which contains "MyNamespace" string.

// we used Split before
$filter->Filter(['contains' => 'MyNamespace']);

// we did not used Split, we want precise item
$filter->Filter(['contains' => 'MyNamspace\\MyClass']);

Note

Filtration is made in cascades. So you can filter filtered data simply by calling another Filter().

Note

"contains" looks for value of the same type ('5' does not find 5). Unknown command throws FilterException.

Tip

You can reset filter anytime (to work on same dataset) with calling ResetFilter().

Now we can receive filtered dataset.

// gets filtered data
$fdata = $filter->GetFiltered();

// just print it (before filtration)
$filter->ListAll();

// just print it (after filtration)
$filter->ListFiltered();

Database - connection and query building

To connect to database you need 3 things:

  • connection string
  • username
  • password (can be empty)

Here is example hw to connect:

// you can have connection info defined like this
define('DB_CONNECT_STRING', "mysql:host=localhost;dbname=test_db");
define('DB_CONNECT_USER', 'root');
define('DB_CONNECT_PASS', '');

// crete configuration
$conconf = new DbConnectionConfig(DB_CONNECT_STRING, new Credentials(DB_CONNECT_USER, DB_CONNECT_PASS));
$conconf->AddAttr(\PDO::ATTR_TIMEOUT, 5); // any PDO attribute you need

// instantiate
$db = new DbConnection($conconf);

Note

Errors are always exceptions (DbException). Original exception is kept inside - $exc->GetInner().

Lets CRUD this thing, starting with READ. You need to provide table name and selection information if needed.

// simply select all data from table
return $db->Query($table)->Select()->Execute();
// lets use simple where clause (for every ? use [value1,...]
return $db->Query($table_users)->Where('id = ?', [1])->Select(['name'])->Execute();
// more advanced but still primitive usage of where
// AND
return $db->Query($table)->Where('id = ?', [1])->AndWhere('id = ?', [1])->Select(['id'])->Execute();
// OR
return $db->Query($table)->Where('id = ?', [1])->OrWhere('id = ?', [2])->Select(['id'])->Execute();

Note

Calling Where again joins with AND, so you can add conditions in a loop.

Order and limit:

return $db->Query($table)->Select()->OrderBy(['name' => 'DESC'])->Limit(10)->Execute();

Warning

Table name, column names in Insert/Update and OrderBy are checked and quoted. Columns given to Select are raw SQL (so COUNT(*) AS count works) - never put anything from request there.

Now lets INSERT something

// returns number of affected rows
$db->Query($table)->Insert([
    'book_name' => 'Chronicles of MissX',
    'book_author' => 'MisterX'
])->Execute();

// id of inserted row
return $db->LastInsertId();

Ok lets UPDATE that last entry

// returns number of affected rows (MySQL counts changed rows only)
$db->Query($table)->Update([
    'book_name' => 'Chronicles of MasterX',
    'book_author' => 'MissX'
])->Where('book_name = ?', ['Chronicles of MissX'])->Execute();

Ok lets delete it. Like real delete it.

// returns number of affected rows
$query = $db->Query($table)->Where('book_author = ?', ['MissX'])->Delete()->Execute();

Something builder can not do? Go raw.

$db->Raw('UPDATE books SET sold = sold + 1 WHERE id = ?', [5]);

Database - connection info

Need to check connection or get some additional info? Lets use previously instantiated $db instance.

$db = new DbConnection($conconf);

// got raw info
$info = $db->ConnInfo();

// obtain firendly formatted info
$arr = $info->AsArray(); // keys are user firendly
$str = $info->AsString();

Debugger

You can debug in style of var_dumping with more control.

First include master class, something like this:

use \Vosiz\VaTools\Debug\Debugger as Debug;

Now you can dump basic things (scalar, array, objects,...)

Tip

Support some objects from package Vosiz/Utils Link package

Now you can dump variables to broswer output.

Debug::Dump($var);

Tip

Include "shorty" and you can you alias "debug"

debug(1);
debug(1.2);
debug(true);
debug('†');
debug("string");
debug(NULL);
debug(1, 5, "str");

class MyClass {

    public      $PublicVar = "this is public";
    protected   $ProtectedVar = "this is protected";
    private     $PrivateVar = "this is private";
}

debug(new MyClass());
debug([1, 2, 3 => 'QWE'])

-- or basic use --

    $var = 1; // anything you want
    Debug::Dump($var);

General - Retval

Return value with type and message, f.e. to pass result of operation up.

use Vosiz\VaTools\Retval;

$retval = Retval::Warning('not a %s problem', 'severe'); // or shorty retval_warning(...)

$retval->Is('warning');     // true
$retval->GetMessage();      // not a severe problem
$retval->GetTypeName();     // Warning
$retval->GetTypeEnum();     // RetvalType enum
echo $retval;               // [Retval.Warning]: not a severe problem

Types: Success, Notice, Info, Warning, Fail, Error, Fatal, Exception, Debug, Fakup.

Format - XML builder

Simple builder of XML documents. Texts and attribute values are escaped for you, names of elements and attributes are checked (XmlException).

use Vosiz\VaTools\Format\XmlBuilder as Xml;

$root = Xml::CreateRoot('response');
$item = Xml::CreateElement('item', 'Tom & Jerry');
$item->AddAtts(['id' => 5]);
$item->SetParent($root);

$xml = new Xml();
$xml->SetRoot($root);

echo $xml->Render();
// <?xml version="1.0" encoding="UTF-8"?>
// <response><item id="5">Tom &amp; Jerry</item></response>

Tip

Do not want the declaration? $xml->SetDeclaration(false)

Format - HTML builder

Same thing for HTML, with factory for common elements.

use Vosiz\VaTools\Format\HtmlBuilder as Html;

$html = new Html();
$html->AddToBody([
    Html::H(1, 'Hello'),
    Html::P('1 < 2'),
    Html::A('click me', '/path')->SetClass('button'),
    Html::Ul(['one', 'two']),
    Html::Table([['Alice', 30], ['Bob', 25]], ['Name', 'Age']),
]);

echo $html->Render(); // whole document with DOCTYPE

Factory: A, Button, Div, H, Input, Li, Ol, P, Table, Tr, Ul. Anything else with Html::CreateElement('tag', 'text').

Tests

./tests.sh

Runs PHPUnit in Docker on PHP 7.4 and 8.2, nothing but Docker is needed. Arguments go to PHPUnit (./tests.sh --filter Flagword), PHP_VERSIONS=8.2 ./tests.sh picks the version.