Showing entries with tag "PHP".

Found 30 entries

PHP: Disable PrivateTmp

Rocky Linux uses PrivateTmp to give each process it's own walled off section of /tmp/. This is good for security sometimes, but it can also lead to frustration because files written to /tmp/ or /var/tmp/ will not show up when you need to debug. To disable PrivateTmp for php-fpm you need to modify the systemd configuration for php-fpm. This is done by running systemctl edit php-fpm and adding a section that says:

[Service]
PrivateTmp=false

Once the change is in place you will need to systemctl daemon-reload and then restart systemctl restart php-fpm.

Note: Borrowed from Stack Overflow.

Leave A Reply

Keeping Apache and PHP from leaking version information

PHP and Apache like to advertise themselves in the HTTP headers of every connection. It's probably best not to advertise your versions. You can disable that with these configuration entries:

# In /etc/php.ini
expose_php = Off
# In your Apache config
ServerTokens Prod
ServerSignature Off
Leave A Reply

SuperGenPass is great

I'm a big fan of SuperGenPass so I decided to learn how it works. The algorithm is pretty simple so I decided to implement it in two of my favorite languages: PHP and Perl.

Hopefully this will help someone else trying to understand the concepts. Special thanks to Iannz for the great Bash implementation I based my code on.

Leave A Reply

PHP: Creation of dynamic property Class::$Var is deprecated

I upgraded to PHP 8.2 today and started getting a lot of Creation of dynamic property Class::$Var is deprecated errors. This is just PHP's way of telling you that you need to pre-declare your class variables before you use them. To me this just meant adding a lot of:

class user {
    var $name  = "";
    var $id    = 0;
    var $perms = [];

to the tops of my classes. This is the right way to fix things, and what I did in 99% of the cases where I was seeing this error.

However I do have a couple of classes where it's necessary to be able to add dynamic properties on the fly, and PHP does offer a work around for this. If you use a special declaration immediately before your class statement PHP will allow dynamic properties to be added without throwing any errors.

#[\AllowDynamicProperties]
class user {

Note: The \ in the brackets is required

Leave A Reply

PHP: Serializing data to save it for caching

I need to cache some data to disk between PHP requests so I decided to compare the various methods available.

Using 100000 element array as source data

Serialize save: 2.958 ms (1.5M)
Serialize read: 5.447 ms

JSON save: 1.880 ms (574.96K)
JSON read: 6.876 ms

PHP save: 8.684 ms (1.7M)
PHP read: 26.863 ms

Memcache set: 5.651 ms
Memcache get: 2.465 ms

IGBinary save: 1.377 ms (720.08K)
IGBinary read: 2.245 ms

MsgPack save: 1.389 ms (359.92K)
MsgPack read: 2.930 ms

I was surprised to see IGBinary and MsgPack so much faster than native JSON. JSON is easy and super portable, but not the fastest.

Leave A Reply

PHP: Stopwatch function to time pieces of code

I need to time how long a certain piece of code takes to complete. I wrote a simple PHP stopwatch function to accomplish this:

sw(); // Start the stopwatch
$x  = some_slow_code();
$ms = sw(); // Return milliseconds since start
// Stopwatch function: returns milliseconds
function sw() {
    static $start = null;

    if (!$start) {
        $start = hrtime(1);
    } else {
        $ret   = (hrtime(1) - $start) / 1000000;
        $start = null; // Reset the start time
        return $ret;
    }
}
Leave A Reply

PHP: Find the second (or third) occurrence of a substr in a string

I needed to find the second occurrence of a substring inside of a larger string. PHP has strpos() which gets you the first occurrence, but nothing beyond that. I wrote a wrapper function around strpos() to let you specify the number you want to find. Returns false if nothing is found.

function strpos_num(string $haystack, string $needle, int $num) {
    $offset = 0;
    $length = strlen($needle);
    $pos    = null;

    for ($i = 0; $i < $num; $i++) {
        $pos = strpos($haystack, $needle, $offset);

        // Short circuit continued lookups if we don't find anything
        if ($pos === false) { return false; }

        $offset = $pos + $length;
    }

    return $pos;
}
Leave A Reply

PHP: Using __halt_compiler()

PHP has a mechanism that allows you stop the compiler on a given line. This allows you to put non-PHP code after that line without affecting your code. You can put data, JSON, HTML, etc after the __halt_compiler() line and then read it with your script. Here is a simple function that reads the text after your __halt_compiler()

// Borrowed from https://gist.github.com/ziadoz/5286233
function get_halt_str() {
    return file_get_contents(__FILE__, false, null, __COMPILER_HALT_OFFSET__ + 1);
}

See also: Perl version

Leave A Reply

PHP: Calculate time difference in human readable way

function human_time_diff(int $seconds) {
    $num  = 0;
    $unit = "";

    if ($seconds < 300) {
        $ret = "just now";
    } elseif ($seconds < 3600) {
        $num  = intval($seconds / 60);
        $unit = "minute";
    } elseif ($seconds < 86400) {
        $num  = intval($seconds / 3600);
        $unit = "hour";
    } elseif ($seconds < 86400 * 30) {
        $num  = intval($seconds / 86400);
        $unit = "day";
    } elseif ($seconds < (86400 * 365)) {
        $num  = intval($seconds / (86400 * 30));
        $unit = "month";
    } else {
        $num  = intval($seconds / (86400 * 365));
        $unit = "year";
    }

    if ($num > 1) {
        $unit .= "s";
    }

    if ($unit) {
        $ret = "$num $unit";
    }

    return $ret;
}

See also: Perl version

Leave A Reply

PHP: Determine if an IP address is part of a given subnet

Given an IP address I need to determine if it is part of a subnet for whitelist/blacklist purposes.

$allowed = ip_in_subnet('65.182.224.40','65.182.224.0/24');
// Borrowed from http://php.net/manual/en/function.ip2long.php#82397
function ip_in_subnet($ip, $cidr) {
    list ($net, $mask) = explode ('/', $cidr);
    return (ip2long ($ip) & ~((1 << (32 - $mask)) - 1)) == ip2long($net);
}

I also ported this function to Perl:

sub ip_in_subnet {
    my ($ip, $cidr)  = @_;
    my ($net, $mask) = split('/', $cidr);

    my $ret = (ip2long ($ip) & ~((1 << (32 - $mask)) - 1)) == ip2long($net);

    return int($ret);
}

Note: You will need the ip2long function.

Leave A Reply

PHP: Serve file for download

If you want to a simple way to serve a file for download in PHP you can use this function:

function serve_file($filepath) {
    $filename = basename($filepath);

    if (headers_sent()) {
        die("Cannot output file because output already started");
    }

    $mime_type = mime_content_type($filepath);
    header("Content-type: $mime_type");
    header("Content-Disposition: attachment; filename=\"$filename\"");

    readfile($filepath);

    exit(0);
}
Leave A Reply

Perl: Loop through an array and extract pairs of variables

I had an array that I wanted to iterate through and extract pairs of variables. I found this pretty neat way to do that:

Perl:

my @arr = ("red", "green", "blue", "yellow", "orange", "purple");

while (@arr) {
    my ($x, $y) = splice(@arr, 0, 2);
    print "$x:$y\n";
}

I found a bunch of different ways to do this, and benchmarked them.

PHP:

$arr = ["red", "green", "blue", "yellow", "orange", "purple"];

while ($arr) {
    [$x, $y] = array_splice($arr, 0, 2);
    print "$x:$y<br />";
}

Note: You need to be careful you have an even number of elements or you will get undefined variable errors.

Leave A Reply

PHP: Either function because PHP lacks a good "or"

PHP lacks a good "or" comparison operator. Neither the or operator, nor the || return a value. I wrote a function that will take two (or more) variables and return the first one that has a non-empty value.

$limit = $array_count || 15;       // true (bad!)
$limit = $array_count or 15;       // true (also bad!)
$limit = either($array_count, 15); // 15 (good!)
function either() {
    $items = func_get_args();

    // Return the first non-empty item
    foreach ($items as $x) {
        if (!empty($x)) {
            return $x;
        }
    }

    return null;
}
Leave A Reply

PHP: Increment a hash variable in an E_NOTICE friendly way

I have a hash that contains counts for a bunch of stats. Unfortunately you can't just increment a hash like: $hash['key']++ and have it be E_NOTICE compliant. If that key does not exist in the array and you try and increment it you will trigger an E_NOTICE alert. I wrote this quick increment implementation that will allow you to increment a non-existing key.


$hash = [];

incr($hash['key']); // Inits to 1
incr($hash['key']); // Sets to 2

// Increment a variable (E_NOTICE compatible)
function incr(&$i, $value = 1) {
    // If the value is already there add to it
    if (isset($i)) {
        $i += $value;
    // If the value isn't there, just set it initially
    } else {
        $i = $value;
    }
}
Leave A Reply

PHP: Set a threshold to limit API access

I have an API that I want to allow outside access to, but it needs to be protected from abuse. I wanted to limit the number of hits from a given source in a way that does not require tracking every single hit in database. Specifically I wanted a method to track millions of sources without requiring insane amounts of disk or memory.

I ended up with the concept of a "bucket" of hits in a given time window. This groups all the hits for a source into a bucket of time which makes it easier to track. My back of the envelope math means that each source should require between 40 and 50 bytes of memory to track. This makes it feasible to track and limit a million clients using about ~50MB of memory. Some type of caching system is required. Memcached, Redis, or a simple in-memory key/value cache would work perfectly.

You can view my proof-of-concept code, and view the raw PHP. This example uses Memcached.

Leave A Reply

PHP: Look in to a multi-dimensional array using array_dive()

I have a multi-dimensional array that I need to extract data from. Given a string in the format of "person.first" I want to descend in to the array looking for person -> first. I wrote this array_dive() function to facilitate this lookup.

function array_dive(string $needle, array $haystack) {
    // Split at the periods
    $parts = explode(".", $needle);

    // Loop through each level of the hash looking for elem
    $arr = $haystack;
    foreach ($parts as $elem) {
        //print "Diving for $elem<br />";
        $arr = $arr[$elem] ?? null;

        // If we don't find anything stop looking
        if ($arr === null) {
            break;
        }
    }

    // If we find a scalar it's the end of the line, anything else is just
    // another branch, so it doesn't cound as finding something
    if (is_scalar($arr)) {
        $ret = $arr;
    } else {
        $ret = null;
    }

    return $ret;
}

Here is a sample script that shows the function in action.

Leave A Reply

PHP: Sanitize a string down to printable characters

I needed a function to make strings into usuable URL identifiers. This function will take an input string and replace all non-url friendly characters with underscores.

function string_sanitize(string $str) {
    // Replace any non-word chars with underscores
    $str = preg_replace("/[\W_]+/", "_", $str);
    // Remove any leading/trailing underscores that are leftover
    $str = trim($str, "_");

    return $str;
}
Leave A Reply

PHP: Convert an array to a hash

I have a flat array that I want to convert to a hash so I can use it as a lookup table. There isn't an easy or clear way to do that in PHP so I wrote my own function:

function array_to_hash(array $array, $val = 1) {
    $ret = array_fill_keys($array, $val);

    return $ret;
}
Leave A Reply

PHP: Calculate the percentage difference between two numbers

I need to compare two numbers and see if they're close to each other. Specifically I wanted to see if two numbers were within 3% of of each other. I wrote this simple function to calculate the percentage difference between two numbers, and optionally (with the third parameter) return true or false if they're within a given range. This should allow me to do a "fuzzy compare" on two numbers.

function percent_diff($a, $b, $ok_per = null) {
    $per_diff = abs((1 - ($a / $b)) * 100);

    if (is_numeric($ok_per)) {
        $ok = $per_diff < $ok_per;

        return $ok;
    }

    return $per_diff;
}
Leave A Reply

PHP: Disable output buffering for the CLI

When writing a PHP application that's going to be run from the CLI often you will want to disable output buffering. With output buffering enabled you will not see any output until your terminal buffer (usually 1k or 4k) fills up . This is accomplished with the following PHP code at the beginning of your script:

ob_get_flush();
Leave A Reply

PHP: IPV6 reverse DNS entries

Reverse DNS (PTR) entries in IPV6 are different than their IPV4 counterparts. To create an IPV6 reverse entry, you have to: fully expand the address, reverse it, and add a period between each character.

For example: 2001:db8::60 reverses to 0.6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.

I wrote a simple PHP function to handle this for me:

function ipv6_ptr($ip_str) {
    $hex = unpack("H*hex", inet_pton($ip_str));
    $str = strrev($hex['hex']);
    $p   = str_split($str);
    $ret = join(".",$p);

    return $ret;
}
Leave A Reply

PHP: is_exception()

I have an object in PHP that I need to check if it is an exception and act appropriately. Surprisingly PHP does not have an is_exception() function built in so I had to write my own:

function is_exception($obj,$strict = false) {
    if (!is_object($obj)) {
        return false;
    }

    // It's some type of exception
    if ($obj instanceof Exception) {
        if (!$strict) {
            return true;
        // It's a raw exception
        } elseif (get_class($obj) === "Exception") {
            return true;
        }
    }

    return false;
}

If you pass true as the second parameter it will only return true if it's a raw exception and not an inherited exception.

Leave A Reply

PHP: Relative path between two directories

I need to calculate the relative path between two directories such that:

$a = "/one/two/three/four/";
$b = "/one/two/";

$a to $b has a relative path of ../../ and $b to $a has a relative path of three/four/. This function solves that problem very simply:

// Borrowed from http://php.net/manual/en/function.realpath.php#105876 and cleaned up
function relativePath($from, $to, $ps = DIRECTORY_SEPARATOR) {
    $arFrom = explode($ps, rtrim($from, $ps));
    $arTo   = explode($ps, rtrim($to, $ps));

    while(count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) {
        array_shift($arFrom);
        array_shift($arTo);
    }

    return str_pad("", count($arFrom) * 3, '..' . $ps) . implode($ps, $arTo);
}
Leave A Reply

PHP: flat_var_export()

I needed to log a nested array to a file. To do this I needed to convert the array to a single line. After some searching I found improved_var_export() that converts a PHP array/object to a single line textual representation. I cleaned up some of the formatting and this is the result.

function flat_var_export($variable, $return = false) {
    if ($variable instanceof stdClass) {
        $result = '(object) ' . flat_var_export(get_object_vars($variable), true);
    } elseif (is_array($variable)) {
        $array = array();
        foreach ($variable as $key => $value) {
            $array[] = var_export($key, true) . ' => ' . flat_var_export($value, true);
        }
        $result = 'array(' . implode(', ', $array) . ')';
    } else {
        $result = var_export($variable, true);
    }

    if ($return) {
        return $result;
    } else {
        print $result;
    }
}
Leave A Reply

PHP: Quote Word

I needed a function similar to Perl's qw. If you pass a string to this function it will return an array of the words, stripping any separating whitespace. If you pass true as the second parameter you will instead get a hash returning each word in a key/value pair.

function qw($str,$return_hash = false) {
    $str = trim($str);

    // Word characters are any printable char
    $words = str_word_count($str,1,"!\"#$%&'()*+,./0123456789-:;<=>?@[\]^_`{|}~");

    if ($return_hash) {
        $ret = array();
        $num = sizeof($words);

        // Odd number of elements, can't build a hash
        if ($num % 2 == 1) {
            return array();
        } else {
            // Loop over each word and build a key/value hash
            for ($i = 0; $i < $num; $i += 2) {
                $key   = $words[$i];
                $value = $words[$i + 1];

                $ret[$key] = $value;
            }

            return $ret;
        }
    } else {
        return $words;
    }
}

This is useful in the following scenarios:

$str  = "Leonardo    Donatello    Michelangelo    Raphael";
$tmnt = qw($str);

$str = "
    Leonardo       Blue
    Donatello      Purple
    Michelangelo   Orange
    Raphael        Red
";
$turtles = qw($str,true);

Here is a similar function written in Python 3.x:

xarray = qw("reg blue green orange yellow")
def qw(xstr):
    ret = xstr.strip().split()

    return ret
Leave A Reply

PHP: Assign a default value to a variable

PHP E_ALL causes a warning to be thrown if you read an unitialized value from an array. For example if you did $debug = $_GET['debug']; but debug is not present in $_GET.

This function will conditionally assign a value to a variable that will not cause a warning to be thrown. It also has the added benefit of having a default option that you can specify.

function var_set(&$value, $default = null) {
    if (isset($value)) {
        return $value;
    } else {
        return $default;
    }
}

This allows us to safely assign a variable from the superglobals.

# Default of null if not in array
$debug = var_set($_GET['debug']);

# Specific default of 99
$level = var_set($_GET['level'],99);

Update: PHP 7+ has null coalesce built in which is better than this. $level = $_GET['level'] ?? 99

Leave A Reply

Regexp for spliting on non-escaped characters

I have a string like the following:

desc=Did you know 2 + 2 \= 4?

I want to split each chunk of that string into segments separated by the equal signs. I can't just split on the equal signs because the text has an equal sign in it. I need to split on the non-escaped equal signs.

@parts = split(/(?<!\\)=/,$str);

This is called positive and negative look behind.

In PHP the only difference is that you have to double escape your \

$parts = preg_split('/(?<!\\\\)=/',$str);
Leave A Reply

Perlfunc: pfile()

PHP has a handy function named file() that will read the contents of a file into a variable. I wrote a quick Perl version of the same function.

sub pfile {
    my $target = shift();
    my $is_fh  = defined(fileno($target));
    my $ret;

    # If we passed in a FH read everything from that
    if ($is_fh) {
        while (readline($target)) { $ret .= $_; }
    # Else it's a file to be opened 
    } else {
        open (my $fh, "<", $target) or return undef;

        while (<$fh>) { $ret .= $_; }
    }

    if (wantarray) {
        return split('\n',$ret);
    }

    return $ret;
}
Leave A Reply - 1 Reply

Remove an item from a PHP array

Given an array in PHP, I need to remove a specific element (if it exists in the array) from the array. The following is a pretty simple and elegant solution. I didn't use unset on purpose because you have to check for a integer result before you remove the item from the array.

$arr     = array('apple','banana','orange');
$new_arr = array_diff($arr, array('banana'));

print_r($new_arr);
Leave A Reply

Perlfunc: human_size()

Quick function to convert bytes to a human readable string:

my $str = human_size(1536);       # "1.5K"
my $str = human_size(1234567);    # "1.2M"
my $str = human_size(1234567890); # "1.1G"
sub human_size {
    my $size = shift();
    if (!defined($size)) { return undef; }

    if    ($size >= (1024**5) * 0.98) { $size = sprintf("%.1fP", $size / 1024**5); }
    elsif ($size >= (1024**4) * 0.98) { $size = sprintf("%.1fT", $size / 1024**4); }
    elsif ($size >= (1024**3) * 0.98) { $size = sprintf("%.1fG", $size / 1024**3); }
    elsif ($size >= (1024**2) * 0.98) { $size = sprintf("%.1fM", $size / 1024**2); }
    elsif ($size >= 1024)             { $size = sprintf("%.1fK", $size / 1024);    }
    elsif ($size >= 0)                { $size = sprintf("%dB"  , $size);           }

    return $size;
}

Here is the same function implemented in PHP:

function human_size($size) {
    # If the size is 0 or less, return 0 B this stops math errors from occurring
    if ($size <= 0) {
        return '0B';
    } else {
        $unit=array('B','K','M','G','T','P');
        return @round($size/pow(1024,($i=floor(log($size,1024)))),2) . $unit[$i];
    }
}

The same function in C

char buf[8] = "";

human_size(348, buf);
printf("Got: %s\n", buf);

char* humanSize(unsigned long long size, char* str) {
    if (size > 1152921504606846976L) {
        snprintf(str, 7, "%.1fE", (float)size / 1152921504606846976L);
    } else if (size > 1125899906842624L) {
        snprintf(str, 7, "%.1fP", (float)size / 1125899906842624L);
    } else if (size > 1099511627776L) {
        snprintf(str, 7, "%.1fT", (float)size / 1099511627776L);
    } else if (size > 1073741824L) {
        snprintf(str, 7, "%.1fG", (float)size / 1073741824L);
    } else if (size > 1048576L) {
        snprintf(str, 7, "%.1fM", (float)size / 1048576L);
    } else if (size > 1024) {
        snprintf(str, 7, "%.1fK", (float)size / 1024);
    } else if (size <= 1024) {
        snprintf(str, 7, "%uB", (unsigned)size);
    }

    return str;
}
Leave A Reply - 1 Reply