initial code commit

This commit is contained in:
Victor Westerlund 2023-03-16 17:04:11 +01:00
parent ca16497b80
commit 4663cf90e4
4 changed files with 215 additions and 0 deletions

48
.gitignore vendored Normal file
View file

@ -0,0 +1,48 @@
# Bootstrapping #
#################
/node_modules
/public/hot
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.phpunit.result.cache
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Icon?
ehthumbs.db
Thumbs.db
.directory
# Tool specific files #
#######################
# vim
*~
*.swp
*.swo
# sublime text & textmate
*.sublime-*
*.stTheme.cache
*.tmlanguage.cache
*.tmPreferences.cache
# Eclipse
.settings/*
# JetBrains, aka PHPStorm, IntelliJ IDEA
.idea/*
# NetBeans
nbproject/*
# Visual Studio Code
.vscode
# Sass preprocessor
.sass-cache/

92
README.md Normal file
View file

@ -0,0 +1,92 @@
# FunctionFlags
A library that aims to make it a bit easier to use PHP constants as function/method flags.
Flags can be defined and checked globally by calling `FunctionFlags`'s static methods.
```php
use FunctionFlags/FunctionFlags
// Define global flags for use anywhere for this runtime
FunctionFlags::define([
"MY_FLAG",
"OTHER_FLAG"
]);
// Returns true if MY_FLAG is passed
function foo(int $flags = null): bool {
return FunctionFlags::isset(MY_FLAG);
}
foo(MY_FLAG); // true
foo(OTHER_FLAG|MY_FLAG); // true
foo(OTHER_FLAG); // false
foo(); // false
```
Flags can also be scoped to a class by creating a new `FunctionFlags` instance. Only flags defined in this instance will be matched
```php
use FunctionFlags/FunctionFlags
$my_flag = new FunctionFlags("MY_FLAG");
$other_flag = new FunctionFlags("OTHER_FLAG");
// Returns true if MY_FLAG is passed and present in $my_flag
function foo(int $flags = null): bool {
return $my_flag->isset(MY_FLAG);
}
foo(MY_FLAG); // true
foo(OTHER_FLAG|MY_FLAG); // true
foo(OTHER_FLAG); // false
foo(); // false
```
## Installation
Requires PHP 8.1 or newer
1. **Install composer package**
```
composer require victorwesterlund/functionflags
```
2. **Include FunctionFlags in your project**
```php
use FunctionFlags/FunctionFlags
```
3. **Define some flags** (Using static approach for demo)
```php
FunctionFlags::define([
"MY_FLAG",
"OTHER_FLAG"
]);
```
4. **Add a function which accepts flags**
```php
// 1. If your function takes more than 1 argument. The "flags" variable MUST be the last.
// 2. It's recommended to make your "flags" variable default to some value if empty to make flags optional.
function foo($bar = null, int $flags = null): bool {
return FunctionFlags::isset(MY_FLAG);
}
```
5. **Use flags in function calls**
```php
// Your function can now accept flags. One or many using the Union operator `|`
foo("hello world", OTHER_FLAG|MY_FLAG);
```
## Methods
Static|Description
--|--
`FunctionFlags::define(string\|array)`|Flag(s) to define as string or array of string. This method must be called before using the flag(s).
`FunctionFlags::isset(constant)`|The constant you wish to check is set on your function or method call.
Instanced|Description
--|--
`new FunctionFlags(string\|array\|null)`|Flag(s) to define as string or array of string. This method must be called before using the flag(s).
`FunctionFlags->define(string\|array)`|Flag(s) to define as string or array of string. This method must be called before using the flag(s).
`FunctionFlags->isset(constant)`|The constant you wish to check is set on your function or method call.

19
composer.json Normal file
View file

@ -0,0 +1,19 @@
{
"name": "victorwesterlund/functionflags",
"description": "An attempt to tame PHP constants for use as function/method flags",
"type": "library",
"license": "GPL-3.0-only",
"authors": [
{
"name": "Victor Westerlund",
"email": "victor.vesterlund@gmail.com"
}
],
"minimum-stability": "dev",
"autoload": {
"psr-4": {
"FunctionFlags\\": "src/FunctionFlags/"
}
},
"require": {}
}

View file

@ -0,0 +1,56 @@
<?php
class FunctionFlags {
// Limit value for debug_backtrace()
private static $trace_limit = 5;
// Reserve a constant address space
private static function addr_reserve(): int {
// Set initial value when first flag is defined
if (empty($_ENV[__CLASS__])) {
$_ENV[__CLASS__] = 0;
}
// Increment counter
$_ENV[__CLASS__]++;
// Return counter as power of 2
return $_ENV[__CLASS__] ** 2;
}
// Get flags from caller closest to this method on the call stack
private static function get_flags_from_caller(): int|null {
// Get call stack
$stack = debug_backtrace(0, (__CLASS__)::$trace_limit);
// Extract class names from callstack in reverse order and find the first occurance of this class in the backtrace
$idx = array_search(__CLASS__, array_reverse(array_column($stack, "class")));
// Failed to locate this class in a full backtrace
if ($idx === false) {
throw new Exception("Failed to retrieve flags from initator callable");
}
// Get args array from stack entry by index
$args = $stack[$idx]["args"];
return !empty($args) ? end($args) : null;
}
/* ---- */
// Define new constants
public static function define(string|array $flags) {
// Convert to array
$flags = is_array($flags) ? $flags : [$flags];
// Define constant for each flag with unique address
foreach ($flags as $flag) {
define($flag, (__CLASS__)::addr_reserve());
}
}
// Check if a flag is set with bitwise AND of all flags
public static function isset(int $flag): bool|null {
$flags = FunctionFlags::get_flags_from_caller();
return $flags ? $flags & $flag : null;
}
}