PHPackages                             phred/phred - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. phred/phred

ActiveProject

phred/phred
===========

Phred

315169[1 PRs](https://github.com/nazariyg/Phred/pulls)PHP

Since Mar 27Pushed 3y ago18 watchersCompare

[ Source](https://github.com/nazariyg/Phred)[ Packagist](https://packagist.org/packages/phred/phred)[ RSS](/packages/phred-phred/feed)WikiDiscussions master Synced 5d ago

READMEChangelogDependenciesVersions (1)Used By (0)

 [![](readme-files/phred-logo.png)](readme-files/phred-logo.png)

---

Phred is an open-source initiative aimed at providing PHP with a consistent, completely object-oriented coding standard that enjoys a comfortable API for creating modern-day web applications with native support for Unicode, with components for internationalization and localization, clear-cut fundamental data types focused on performance and reliability, enhanced testing and debugging, and other features.

Phred is also outlining a web application framework to let developers take the most advantage of the improved PHP in their projects as well as to ensure backward compatibility with the whole multitude of the existing PHP libraries and APIs.

One of the Phred's prime efforts is to maintain a clear and thorough [documentation](http://htmlpreview.github.com/?https://github.com/nazariyg/Phred/blob/master/doc/index.html).

At a Glance
-----------

[](#at-a-glance)

The OOP magic below is not natively supported by PHP but is available with Phred:

```
// An OOP Unicode string.
$str = "Юнікод Ооп";

echo $str->length();        // 10
echo $str->toUpperCase();   // ЮНІКОД ООП

$array = $str->split(" ");
echo $array->join(", ");    // Юнікод, Ооп
$array->sortOn("length");
echo $array->join(", ");    // Ооп, Юнікод

echo strlen($str);          // 19
echo strtoupper($str);      // Юнікод Ооп
```

---

> *Phred is still young and the currently implemented object-oriented library and framework features are not yet RC. Class names, method names, and other details need to be tuned in to the best preferences of PHP developers. And Phred might branch into an OOP library and a separate framework. But at this point, Phred mainly requires code contributions to enable PHP developers with the benefits of OOP and native Unicode support by the time when PHP 7 arrives. Hopefully, the advanced extension capabilities of PHP 7 will allow for more seamless backward compatibility and dedicated syntax for data structures.*

> **Phred might be looking for a new owner. If you are someone who would take a good care of it, contact me at **

---

- [Preface](#preface)
    - [PHP 6 or Uniphant vs. Elecorn](#php-6-or-uniphant-vs-elecorn)
    - [PHP 7 or Much Like PHP 5.6+ but Faster](#php-7-or-much-like-php-56-but-faster)
    - [こんにちは, Phred](#%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF-phred)
    - [The Zen of Phred](#the-zen-of-phred)
- [Installation](#installation)
    - [Getting the Latest PHP Version](#getting-the-latest-php-version)
    - [Installing Phred](#installing-phred)
    - [Running Unit Tests](#running-unit-tests)
- [Fundamental Types](#fundamental-types)
    - [OOP String](#oop-string)
        - [Regular Expressions](#regular-expressions)
        - [Character Encodings](#character-encodings)
    - [OOP Array](#oop-array)
    - [OOP Map](#oop-map)
    - [Time and Time Zones](#time-and-time-zones)
    - [Comparison and Sorting](#comparison-and-sorting)
- [Enhanced Testing and Debugging](#enhanced-testing-and-debugging)
    - [Semantic Checks](#semantic-checks)
    - [Bug Tracking and Reporting](#bug-tracking-and-reporting)
- [U14n](#u14n)
    - [Locales](#locales)
    - [Locale-Specific Formatting](#locale-specific-formatting)
- [Batteries Included](#batteries-included)
    - [Input Filtering](#input-filtering)
    - [Files and Directories](#files-and-directories)
    - [Omnivorous JSON](#omnivorous-json)
    - [Mailing](#mailing)
    - [Requesting](#requesting)
- [Backward Compatibility](#backward-compatibility)

---

Preface
=======

[](#preface)

Any web developer is of course familiar with JavaScript. Highly object-oriented and with Unicode done right, JavaScript just feels comfortable to work in when developing for the client side and JavaScript has even made it to the server side as Node.js. Same kind of sentiments might go to other languages that were also designed with a vision in mind, such as Python, Ruby, and Scala.

But while the quality standards of web development have evolved over the past decade to match the increasing demands for clean, readable, and comprehensible code that can be efficiently shared with other people on the team or made public, PHP has remained mostly immature. This may be especially noticeable if you are a PHP developer trying to create an innovative web application that is required to offer a substantial value to many users all over the globe. It even seems bit unfair that PHP, which is the most popular language used by web servers with a share of about [80%](http://w3techs.com/technologies/history_overview/programming_language/ms/y), is not actually superior to JavaScript, even though the control flow of a web application may imply the opposite.

Still being largely procedural and hardly following any conventions in the naming of the daunting number of its functions, some of which start with "str" and some with "str\_", being infamous for the order of "haystack", "needle", and "subject" parameters changing irregularly from one function to another, and without the long overdue support for Unicode, PHP has found itself in a weird relationship with the developers, where the amount of people's love for PHP is mixed with a nearly proportional amount of hate. It is true however that the PHP language is easy to get into thanks to the C-like syntax resembling JavaScript along with a good number of other popular languages and that PHP has become the lingua franca of web programming, with a huge community of developers and open-source contributors. But it's also true that PHP is steadily losing in quality to a bunch of rival languages.

Because PHP is going to stay around for many years to come, the demands for an up-to-date PHP standard can no longer be ignored.

PHP 6 or Uniphant vs. Elecorn
-----------------------------

[](#php-6-or-uniphant-vs-elecorn)

Most of the hopes for a better PHP were being put in PHP 6 announced around 2006. However, PHP 6 became more like a mythical creature over the time, a creature that no one has ever seen. The new features that were planned for PHP, including the highly anticipated Unicode support, had never got released under PHP 6 label. Instead, the PHP 6 development branch was discontinued and only some of the features were backported into the 5.x versions afterwards, still without built-in Unicode. There was no beta or even alpha version of PHP 6.

One of the main reasons behind PHP 6 getting abandoned was a disagreement inside the development team as to which character encoding would serve best for storing and processing Unicode strings. Despite of the fact that the team then picked UTF-16 as the internal encoding to be used for Unicode, the development ran out of steam and eventually came to a halt. This happened not only because the choice of UTF-16 turned out to be suboptimal, but also because of the immense body of work that was required to be done for the PHP's core and all the extensions by the developers becoming less and less enthusiastic with the direction that was chosen and due to other issues. Andrei Zmievski, who was the head of the PHP 6 project, later admitted that he would probably choose UTF-8 over UTF-16 if it was possible to start over.

PHP 7 or Much Like PHP 5.6+ but Faster
--------------------------------------

[](#php-7-or-much-like-php-56-but-faster)

In July of 2014, a vote was held by the PHP's steering group to decide under what name the next major version of PHP should be presented to the public when its development branch, called PHPNG for "PHP New Generation", would become a release candidate. The name that outvoted "PHP 6" and other names was "PHP 7". Skipping over "6" in the PHP's version was probably for the reason that PHP 6 had bit too much of disappointment associated with it and a number of books already existed at the time with "PHP 6" in their titles.

With the advent of PHPNG, which is going to become the basis for PHP 7, the mainstream implementation of PHP made a substantial progress in speed and memory consumption thanks to a great deal of optimization and code refactoring that eliminated numerous bottlenecks in the PHP's performance, while not breaking any backward compatibility. The modified engine also brought PHP closer to being able to benefit from just-in-time compilation in some of its future versions. The benchmarks that were run with PHPNG showed surprisingly good results, indicating almost double increase in speed and significantly smaller memory footprint.

こんにちは, Phred
------------

[](#こんにちは-phred)

The idea of Phred is resonating with the principle of separation of concerns. Let the PHP's core contributors remain focused on the language's underpinnings and further improve the PHP's engine to make it even faster, effectively providing a high-performance foundation upon which a consistent and completely object-oriented coding standard can be implemented by PHP developers themselves.

By extending PHP 7, which is now twice as fast, Phred converts PHP into an up-to-date and clean standard advantageous for creating sophisticated web sites and applications to be used by people all over the world, while keeping performance at a high level. Fortunately, the OOP-related features that are already present in PHP 5.6 and PHP 7 have made this possible. And not to forget the vital OOP infusions into the language that were faithfully made by some of its core contributors, most notably Nikita Popov.

The following is an example of how Phred may look like at work:

```
// Sign up a new user.

$inputUserName = "たかし やまもと ";
$inputBirthday = 491702400;  // converted to Unix time by JS

// Sanitize the username if it's not a valid Unicode string and remove any
// leading or trailing whitespace including any Unicode whitespace.
$userName = $inputUserName->isValid() ? $inputUserName : $inputUserName->sanitize();
$userName = $userName->trim();

if (!$userName->isEmpty()) {
    // The username is not empty.
    if ($userName->length() reFindGroups("/^(\pL+)\s+(\pL+)$/u", $foundGroups)) {
            $firstName = $foundGroups[0];
            $lastName =  $foundGroups[1];
            echo $firstName;  // たかし
            echo $lastName;   // やまもと

            // Transliterate the username into the Latin script for searching.
            $userNameT = $userName->transliterateFromAny("latin")->toLowerCase();
            // "たかし やまもと" becomes "takashi yamamoto".

            // Store the names and the rest of the profile info to the database.
            // ...

            // Let's see how many days we are currently away from the user's birthday.
            $currTime = Tm::now();
            $birthday = new Tm($inputBirthday);
            $currYear = $currTime->yearUtc();
            $bdMonth =  $birthday->monthUtc();
            $bdDay =    $birthday->dayUtc();
            if (Tm::areComponentsValid($currYear, $bdMonth, $bdDay)) {
                $bdThisYear = Tm::fromComponentsUtc($currYear, $bdMonth, $bdDay);
                if ($currTime->diffInDays($bdThisYear) substr(0, 0);  // "" (correct, same as in JS)
echo $str->substr(5, 0);  // "" (correct, same as in JS)
```

Other methods that do handle special cases with care are `indexOf` and `indexOfCi`, `lastIndexOf` and `lastIndexOfCi`, `split`, `insert`, and several more.

### Regular Expressions

[](#regular-expressions)

Phred greatly simplifies the use of regular expressions in PHP by integrating the regular expression functionality right into OOP strings.

This integration is intended to save you lots of typing by getting rid of the overly complicated `preg_` functions, `=== 1` and `=== 0` when searching for regular expression patterns since the appropriate string methods naturally return either `true` or `false`, by outputting the found string directly instead of unnecessarily enfolding it into an array, and by avoiding the intricacies with obscure multidimensional arrays when searching for regular expression groups.

Any string method that has to do with regular expressions indicates it with `re` in the beginning of its name. The `re` methods that behave along the lines of some of the plain-text methods are trying to follow maximally the naming and the parameter order used by their counterparts. The only big difference however is `Ci` in the method naming because, as imposed by PCRE, the case-insensitive mode can only be turned on with the "i" modifier in the regular expression pattern itself.

Most of the complexity that exists with regular expression searches was divided and conquered by combining the possible kinds of searches based on whether the search starts from the beginning of the string or from a specific position, whether the search targets a substring or regular expression groups, and whether the search stops after the first occurrence of the regular expression pattern is found or continues to find all the occurrences.

The following example searches for regular expression groups in a string and stores found strings to arrays:

```
$str = "[gr0-0] [gr0-1] [gr1-0] [gr1-1]";
$numOccurFound = $str->reFindAllGroups("/(\[.*?\])\s+(\[.*?\])/", $foundGroups,
    $foundStrings);
echo $numOccurFound;           // 2
echo $foundGroups[0][0];       // "[gr0-0]"
echo $foundGroups[0][1];       // "[gr0-1]"
echo $foundGroups[1][0];       // "[gr1-0]"
echo $foundGroups[1][1];       // "[gr1-1]"
echo $foundStrings->length();  // 2
echo $foundStrings[0];         // "[gr0-0] [gr0-1]"
echo $foundStrings[1];         // "[gr1-0] [gr1-1]"
```

For a challenge, you could try doing the same but with `preg_` functions just to see the amount of needless code it would produce.

### Character Encodings

[](#character-encodings)

Converting from one character encoding to another and other typical tasks related to character encodings are taken care of by the [CEString](http://htmlpreview.github.com/?https://github.com/nazariyg/Phred/blob/master/doc/classes/CEString.html) class (alias `EStr`).

Powered by the ICU library, the class is able to convert between practically all character encodings there are in existence. For historical and other reasons, almost every of the character encodings supported by ICU is known by more than one name. Because of this, ICU picked a single name for each character encoding by which the encoding is to be identified internally. Such character encoding names are called primary by the class and the rest of the names are aliases. For example, "UTF-8" is a primary character encoding name, whereas "ibm-1208" and "cp1208" are some of its many aliases, and it's safe to say that "UTF-8" is an alias of "cp1208". When you need to tell a character encoding name to a method of the class, an alias is just as good as the primary name.

In addition to converting between character encodings, detecting encodings, and fixing UTF-8 strings, the class can translate any Unicode string written in any language into ASCII, while preserving as much information as possible. This "flattening" to ASCII might be useful for the indexing of Unicode text, for searching inside or with Unicode text, and for collating Unicode text. If required, the string is transliterated into the Latin script beforehand. Latin characters such as "æ" and German sharp "ß" are all handled properly ("æ" becomes "ae" and "ß" becomes "ss"). The process also converts to ASCII some of the beyond-Latin Unicode characters that have similar appearance or meaning. The Unicode's Line Separator and Paragraph Separator characters are converted into ASCII's LF characters.

OOP Array
---------

[](#oop-array)

Phred draws a clear distinction between apples and oranges by bringing forward a highly efficient array type for storing and accessing elements with minimum overhead. The type wraps over the SplFixedArray class offered by the Standard PHP Library, which is an integral part of PHP and is written in C. The OOP array is implemented in the [CArrayObject](http://htmlpreview.github.com/?https://github.com/nazariyg/Phred/blob/master/doc/classes/CArrayObject.html) class (alias `Ar`).

It often comes as a surprise to new PHP developers that PHP only has one data type to be used for all kinds of arrays. And any PHP array is an associative array, otherwise known as dictionary, hash map, or map. The PHP array is only able to store a value if the value brings along a key by which the value can then be accessed. Therefore, a PHP array is always storing key-value pairs instead of just values, even for simple arrays. In contrast, JavaScript provides the developer with the choice between `Array` type for simple arrays and `Object` type for associative arrays, while Python offers an assortment of at least four array types, namely `tuple`, `list`, `dict`, and `set`.

It would be really nice if PHP developers could benefit from this all-in-one design decision, but unfortunately they don't. On average, a programmer has to work with simple arrays more than with associative ones and simple arrays may contain large quantities of values that need to be stored with least memory consumption. So not only PHP arrays are biased towards one array use at the expense of the other use, but they are also biased in the wrong direction.

Furthermore, the way PHP is using term "array" is outright misleading since what any PHP array is really behaving as is an ordered (hash) map. This misconception has been the source for generating a great deal of confusion in the PHP docs on the array functions and made some of the PHP array's behavior appear so bizarre that, in the eyes of many PHP developers, it may look hardly distinguishable from bug reports.

The following examples illustrate how much unpredictable PHP arrays can be:

```
$assocArray = array();
$assocArray[0] = "a";
$assocArray[1] = "b";
$assocArray[2] = "c";

// At some point, remove a no longer needed element and replace it with
// a new element at some point later.
unset($assocArray[1]);
// ...
$assocArray[1] = "d";

echo implode(", ", $assocArray);  // "a, c, d"
```

Another example:

```
$assocArray = array();

// We don't know the value of one of the elements yet, will assign it later.
$assocArray[0] = "a";
$assocArray[2] = "c";
// ...
// OK, we know the value now.
$assocArray[1] = "b";

echo implode(", ", $assocArray);  // "a, c, b"
```

And most notoriously:

```
$assocArray = array();
for ($i = 4; $i >= 0; $i--) {
    $assocArray[$i] = $i;
}

echo implode(", ", $assocArray);  // "4, 3, 2, 1, 0" instead of "0, 1, 2, 3, 4"
```

But the good thing is that the array implementation being forced by PHP on developers is not the only one available. Another is the [SplFixedArray](http://www.php.net/SplFixedArray) class included with the PHP's SPL library, the goal of which is to provide efficient implementations for interfaces and classes that have to do with data structures in PHP. The language behind the SplFixedArray class' implementation is C. Despite of the class' name, the length of an SplFixedArray is not really fixed and an SplFixedArray can be easily resized using a dedicated method of the class. In addition to arrays, SPL also supplements PHP with classes for lists, stacks, and queues (Phred might acquire those with time).

In Phred, the SplFixedArray class serves as the underlying array implementation for any OOP array. This allows arrays in your applications to be very lightweight and efficient. And it also takes all the bad surprises out of interacting with arrays in your code since an OOP array *always* behaves just like you would expect it to.

As a test, let's allocate some number of integer values, first with a PHP array and then with an OOP array:

```
$assocArray = array();
for ($i = 0; $i < 500000; $i++) {
    $assocArray[$i] = 0;
}

echo memory_get_peak_usage();  // 74209240
```

And when using an OOP array:

```
$array = new Ar(500000);
for ($i = 0; $i < 500000; $i++) {
    $array[$i] = 0;
}

echo memory_get_peak_usage();  // 30017960
```

The difference in the peak numbers of allocated bytes shows about 60% of memory saving when an OOP array is used. This is an enjoyable improvement in performance, even for already boosted PHP 7.

So what we've got is:

- OOP arrays have much smaller memory footprint because their elements are stored sequentially and in a much more dense fashion.
- Because the elements in an OOP array are indexed with integer numbers, it allows for significantly faster element access in both reading and writing.
- OOP arrays enable you with the freedom of choice and let you make better optimization decisions in your applications.
- OOP array behaves much like its counterparts from the languages that you are familiar with, such as JavaScript.
- Exactly like in JavaScript, any OOP array is an object that is stored, assigned, and passed by reference.

You can create a new OOP array as an empty array to be grown later, as an array with a number of pre-allocated elements ready to be assigned, or from existing values to become the array's elements. The first two scenarios are covered by the constructor of the OOP array's class:

```
// Create an empty array.
$array = new Ar();
echo $array->length();  // 0

// Create an array with pre-allocated elements.
$array = new Ar(15);
echo $array->length();  // 15
```

For creating an OOP array from a list of values, you would use a syntax that is shorter than `array()` by 4 characters:

```
$array = a("one", "two", "three", "four", "five");
echo $array->length();  // 5
```

Something that is true for any array implementation is that, if the length of an array you are creating is known beforehand, it is faster to pre-allocate the array's elements first and then assign them with values as compared to adding the elements one by one using `push` method.

As usual, the elements in an OOP array are accessed with `[ ]` operator:

```
$array = new Ar(5);
for ($i = 0; $i < $array->length(); $i++) {
    $array[$i] = $i*$i;
}

echo $array->join(", ");  // "0, 1, 4, 9, 16"
```

And OOP arrays can be iterated with `foreach` too:

```
$array = a("12", "34", "56");
foreach ($array as $value) {
    echo $value;
}
// "123456"
```

While in JavaScript you would use `slice` method to make an independent copy of an array, with OOP arrays you would rather use `clone` keyword like so:

```
$arrayCopy = clone $array;
```

Some of the methods of the OOP array that need little introduction are `length`, `isEmpty`, `first`, `last`, `push`, `pop`, `slice`, `insert`, `remove`, `splice`, `find`, `filter`, and `sort`.

Inspired by JavaScript, the OOP array also implements `sortOn` method that lets you sort the elements in an OOP array based on return values from a specified method of the elements' class:

```
class ElementClass
{
    protected $myGoodness;

    public function __construct ($goodness) {
        $this->myGoodness = $goodness;
    }
    public function goodness () {
        return $this->myGoodness;
    }
}

$array = a(
    new ElementClass(5),
    new ElementClass(2),
    new ElementClass(4),
    new ElementClass(1),
    new ElementClass(3));
$array->sortOn("goodness");

for ($i = 0; $i < $array->length(); $i++) {
    echo $array[$i]->goodness();
}
// "12345"
```

It's also easier than ever to perform logical operations with arrays using `union`, `intersection`, `difference`, and `symmetricDifference` methods.

OOP Map
-------

[](#oop-map)

As you would expect, an OOP map contains values associated with unique keys. The OOP map's functionality is implemented by the [CMapObject](http://htmlpreview.github.com/?https://github.com/nazariyg/Phred/blob/master/doc/classes/CMapObject.html) class (alias `Ma`).

Just like with PHP's associative arrays, a key in an OOP map can be either string or integer. When a string key looks like an integer, it's implicitly converted into the corresponding integer and is used as such to access the key's value in the map.

You can create an OOP map as initially empty or from a list of key-value pairs using a syntax similar to that of OOP arrays only with `m` in place of `a` and with square brackets to let `=>` operator glue the keys and values together:

```
// Create an empty map.
$map = new Ma();
echo $map->length();  // 0

// Create a map from key-value pairs.
$map = m([
    "a" => "one",
    "b" => "two",
    "c" => "three",
    "d" => "four",
    "e" => "five"]);
echo $map->length();  // 5
```

Naturally, the values in an OOP map are accessible with `[ ]` operator and you can iterate through an OOP map with `foreach`:

```
$map = new Ma();
$map["a"] = "one";
$map["b"] = "two";
$map["c"] = "three";
$map["d"] = "four";
$map["e"] = "five";
foreach ($map as $key => $value) {
    $value = $value->toTitleCase();
    echo "$key:$value ";
}
// "a:One b:Two c:Three d:Four e:Five "
```

Or you can iterate over the values in an OOP map by reference:

```
$map = new Ma();
$map["a"] = "one";
$map["b"] = "two";
$map["c"] = "three";
$map["d"] = "four";
$map["e"] = "five";
foreach ($map as $key => &$value) {
    $value = $value->toTitleCase();
}
foreach ($map as $key => $value) {
    echo "$key:$value ";
}
// "a:One b:Two c:Three d:Four e:Five "
```

Some of the self-describing methods the OOP map are `length`, `isEmpty`, `hasKey`, `remove`, `filter`, `keys`, and `values`. With `valueByPath` and `setValueByPath` methods, you can also access a value in a multi-dimensional map by its key path, which is just a dot-separated sequence of keys that hierarchically indicate the path to the value, as in "level1key.level2key". Unlike the PHP's `array_merge` function, `merge` method is proud of being able to merge multi-dimensional maps correctly so that a value from a subsequent map overrides a value in a preceding map if they are associated with equal keys, making no special cases for numeric keys like `array_merge` function does.

Following the philosophy of JavaScript where the role of the associative array is played by `Object` type, any OOP map is an object in every aspect and is stored, assigned, and passed by reference. But when required, you can make an independent copy of an OOP map using `clone` keyword:

```
$mapCopy = clone $map;
```

Time and Time Zones
-------------------

[](#time-and-time-zones)

Phred tries its best to let you perform all kinds of time-related operations in the simplest way possible. The functionality that is natively present in PHP for dates, time, and time zones and that is mostly scattered over [date/time functions](http://php.net/manual/en/ref.datetime.php), [DateTime class](http://php.net/manual/en/class.datetime.php), [DateTimeZone class](http://php.net/manual/en/class.datetimezone.php), and [IntlTimeZone class](http://php.net/manual/en/class.intltimezone.php) was sorted out, refined, and put into the self-contained and fully-featured [CTime](http://htmlpreview.github.com/?https://github.com/nazariyg/Phred/blob/master/doc/classes/CTime.html) class (alias `Tm`). Time zones are represented by objects of the [CTimeZone](http://htmlpreview.github.com/?https://github.com/nazariyg/Phred/blob/master/doc/classes/CTimeZone.html) class (alias `Tz`).

An object of the CTime class represents a point in time. While there exists a multitude of time zones around the world, any represented point in time is referring to the very same moment in all of the time zones. Clearly, a point in time cannot be defined by the usual year, month, day, hour, minute, and second, because these date/time components vary from one time zone to another. Instead, any point in time is absolutely defined by a single time coordinate called Unix time.

The Unix time is the number of seconds that have elapsed since the midnight of January 1, 1970 as if this moment occurred in the UTC time zone, which is the time zone of the universal time and geographically matches the time zone of Greenwich, UK. UTC used to be called GMT in the past and is still called GMT in some conventions. Internally, the Unix time coordinate of any point in time is stored as a floating-point number, which is positive or zero, unless the point belongs to a year before 1970 and the coordinate is negative. The signed Unix time is also known as "UTime" in the context of the class and the signed number of milliseconds as "MTime".

For example, the Unix time of 1234567890 corresponds to 23:31:30 on February 13, 2009 UTC:

```
$time = new Tm(1234567890);
echo $time->toStringUtc();  // "2009-02-13 23:31:30 UTC"
```

In addition to Unix time, you can create a time object from a formatted string, without even specifying the date/time format that is used by the string:

```
$time = Tm::fromString("2009-02-13 23:31:30");
echo $time->UTime();  // 1234567890
```

And sure enough you can create a time object from date/time components:

```
$year = 2009;
$month = 2;
$day = 13;
$hour = 23;
$minute = 31;
$second = 30;
$time = Tm::fromComponentsUtc($year, $month, $day, $hour, $minute, $second);

echo $time->toStringUtc();  // "2009-02-13 23:31:30 UTC"
```

But if you'd want the values of the date/time components to be tied to the Los Angeles time instead of UTC time:

```
// Create a time zone for Los Angeles.
$timeZone = new Tz("America/Los_Angeles");

$year = 2009;
$month = 2;
$day = 13;
$hour = 23;
$minute = 31;
$second = 30;
$time = Tm::fromComponentsInTimeZone($timeZone,
    $year, $month, $day, $hour, $minute, $second);

echo $time->toStringInTimeZone(
    $timeZone);       // "2009-02-13 23:31:30 America/Los_Angeles"
echo $time->UTime();  // 1234596690 (8 hours difference)
```

Any time zone is primarily identified by its name and the same time zone can have multiple names. Unlike locale names, time zone names are case-sensitive. The names are standardized and, besides other places, can be found in the PHP's [List of Supported Timezones](http://php.net/manual/en/timezones.php).

When you need the current time as a time object, you can create the object with `now` static method:

```
$currTime = Tm::now();
```

The MySQL-like date/time format that you've seen in the previous examples is the default format that, unlike the MySQL format, does not omit the time zone. Other formats are available as well:

```
$time = new Tm(1234567890);

echo $time->toStringUtc(
    Tm::PATTERN_DEFAULT_DATE);     // "2009-02-13"
echo $time->toStringUtc(
    Tm::PATTERN_DEFAULT_TIME);     // "23:31:30"
echo $time->toStringUtc(
    Tm::PATTERN_ISO8601);          // "2009-02-13T23:31:30+0000"
echo $time->toStringUtc(
    Tm::PATTERN_MYSQL);            // "2009-02-13 23:31:30"
echo $time->toStringUtc(
    Tm::PATTERN_RFC822);           // "Fri, 13 Feb 09 23:31:30 +0000"
echo $time->toStringUtc(
    Tm::PATTERN_W3C);              // "2009-02-13T23:31:30+00:00"

$timeZone = new Tz("America/Los_Angeles");
echo $time->toStringInTimeZone($timeZone,
    Tm::PATTERN_MYSQL);            // "2009-02-13 15:31:30"
echo $time->toStringInTimeZone($timeZone,
    Tm::PATTERN_RFC822);           // "Fri, 13 Feb 09 15:31:30 -0800"
echo $time->toStringInTimeZone($timeZone,
    Tm::PATTERN_W3C);              // "2009-02-13T15:31:30-08:00"
```

When dealing with dates and time, the months are identified by their numbers, starting with 1 for January and ending with 12 for December. Hours, minutes, and seconds are numbered in the usual way, starting with 0 and ending with 23 for hours and with 59 for minutes and seconds. Milliseconds are supported for any point in time and their numbers range from 0 to 999. The days of the week are identified by `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, and `SATURDAY` enumerands of the class.

You can ask a time object for individual date/time components like this:

```
$time = Tm::fromString("2009-02-13 23:31:30");

echo $time->yearUtc();       // 2009
echo $time->monthUtc();      // 2
echo $time->dayUtc();        // 13
echo $time->hourUtc();       // 23
echo $time->minuteUtc();     // 31
echo $time->secondUtc();     // 30
echo $time->dayOfWeekUtc();  // Tm::FRIDAY

echo $time->hourInTimeZone(
    new Tz("America/Los_Angeles"));  // 15
```

Or, to make things bit faster, you can obtain the date/time components of a time object in bulk:

```
$time = Tm::fromString("2009-02-13 23:31:30");
$time->componentsUtc($year, $month, $day, $hour, $minute, $second, $millisecond,
    $dayOfWeek);
echo $year;         // 2009
echo $month;        // 2
echo $day;          // 13
echo $hour;         // 23
echo $minute;       // 31
echo $second;       // 30
echo $millisecond;  // 0
echo $dayOfWeek;    // Tm::FRIDAY
```

To find out whether one point in time goes before or after another point in time, you can use `isBefore` and `isAfter` methods:

```
$time0 = Tm::fromString("2009-02-13 23:31:30");
$time1 = Tm::fromString("2009-02-14 23:31:30");
echo $time0->isBefore($time1);  // true
echo $time0->isAfter($time1);   // false
```

Among other methods, the CTime class features the `diff...` group of methods that let you compute the absolute difference between any two points in time measured in one of the seven time units, the `signedDiff...` group of methods that allow for negative differences to be reported, the `shifted...` group of methods that let you shift points in time by a certain amount of time units in either direction, and `with...` group of methods that let you modify a point in time by changing the value of one of its date/time components.

Comparison and Sorting
----------------------

[](#comparison-and-sorting)

Some of the methods of the collection types, which currently are OOP array and OOP map, take as an optional parameter a callback function or method to be used by the method for the inter-comparison of the values contained in the collection so that the method could carry out its mission. You can see the default values for this parameter being either `CComparator::EQUALITY` or `CComparator::ORDER_ASC`, which are strings referring to the methods of the [CComparator](http://htmlpreview.github.com/?https://github.com/nazariyg/Phred/blob/master/doc/classes/CComparator.html) class (alias `Cmpr`). Another such comparator that is available to you is `CComparator::ORDER_DESC`.

With these comparators being the default callbacks, you don't need to worry about how the values in a collection are going to be compared with one another as long as the type of the values is known to the CComparator class. And any scalar or a class that implements one of the Phred's equality/order interfaces is a type that the CComparator class is familiar with.

Naturally, the CComparator class knows how to perform comparisons with each of the fundamental types in addition to scalars, so you can freely put OOP strings, time objects, OOP arrays, and OOP maps into an OOP array and then sort it with `sort` or `sortOn` method or make use of any other method that depends on a comparator, such as `find`, `countElement`, `removeByValue`, `unique`, `isSubsetOf`, `intersection`, and `difference`. With OOP maps, the default comparators make your life easier when you need to use `find` or `countValue` method.

From the CComparator class' point of view, there only exist three kinds of values: those that are scalars (excluding strings), those that conform to the IEqualityAndOrder interface, and those that conform to the IEquality interface. The difference between the interfaces is that objects of the IEquality interface, which only requires `equals` method to be implemented, can only be compared for equality, while objects of the IEqualityAndOrder interface, which requires both `equals` and `compare` methods to be implemented, can also be compared for order. What this means on practice is that you can let objects of your own class be compared with some of the default comparators by implementing either IEqualityAndOrder or IEquality interface for your class.

The types that currently implement the IEqualityAndOrder interface are:

- OOP string
- OOP array
- OOP map
- CTime

And the types that currently implement the IEquality interface are:

- CTimeZone
- CULocale
- CUrl
- CFile

When sorting Unicode strings inside OOP arrays, you've got multiple methods at your disposal. There is `sort` method that would sort your strings using the default or a custom comparator and, in case of `Cmpr::ORDER_ASC` or `Cmpr::ORDER_DESC` being the comparator, the strings would be sorted with all of the available Unicode collation options set to their defaults. The next is `sortOn` method that lets you sort strings based on return values from one of the string methods. And there are methods specifically optimized for string sorting.

The `sortUStrings`, `sortUStringsCi`, `sortUStringsNat`, and `sortUStringsNatCi` methods of the OOP array perform faster when sorting Unicode strings as compared to the more general-purpose `sort` and `sortOn` methods. What else makes these methods special is that, when calling any of the four methods, you can specify the Unicode collation options to be used for string comparison:

```
$array = a("c", "B", "d", "E", "D", "C", "a", "e", "b", "A");
$array->sortUStrings();
// "a", "A", "b", "B", "c", "C", "d", "D", "e", "E"

// Sort the same array with default collation options first and then
// ignoring accents and other marks.
$array = a("č", "B", "d", "E", "D", "C", "á", "ê", "b", "A");
$array->sortUStrings();
// "A", "á", "b", "B", "C", "č", "d", "D", "E", "ê"
$array = a("č", "B", "d", "E", "D", "C", "á", "ê", "b", "A");
$array->sortUStrings(UStr::COLLATION_IGNORE_ACCENTS);
// "á", "A", "b", "B", "č", "C", "d", "D", "ê", "E"

// Sort the same array with default collation options first and then
// ignoring whitespace, vertical space, punctuation, and some symbols.
$array = a(" c", ",B", ".d", ":E", ";D", "!C", "?a", "\"e", "(b", "[A");
$array->sortUStrings();
// " c", ",B", ";D", ":E", "!C", "?a", ".d", ""e", "(b", "[A"
$array = a(" c", ",B", ".d", ":E", ";D", "!C", "?a", "\"e", "(b", "[A");
$array->sortUStrings(UStr::COLLATION_IGNORE_NONWORD);
// "?a", "[A", "(b", ",B", " c", "!C", ".d", ";D", "\"e", ":E"

// Sort the same array with default collation options first and then
// favoring uppercase ahead of lowercase.
$array = a("c", "B", "d", "E", "D", "C", "a", "e", "b", "A");
$array->sortUStrings();
// "a", "A", "b", "B", "c", "C", "d", "D", "e", "E"
$array = a("c", "B", "d", "E", "D", "C", "a", "e", "b", "A");
$array->sortUStrings(UStr::COLLATION_UPPERCASE_FIRST);
// "A", "a", "B", "b", "C", "c", "D", "d", "E", "e"
```

As well as the locale in which the strings should be compared:

```
// Default locale vs. French Canadian locale.
$array = a("cote", "côte", "Côte", "coté");
$array->sortUStrings();
// "cote", "coté", "côte", "Côte"
$array = a("cote", "côte", "Côte", "coté");
$array->sortUStrings(UStr::COLLATION_DEFAULT, new ULoc("fr_CA"));
// "cote", "côte", "Côte", "coté"

// Swedish locale vs. German locale.
$array = a("z", "ö");
$array->sortUStrings(UStr::COLLATION_DEFAULT, new ULoc("sv_SE"));
// "z", "ö"
$array = a("z", "ö");
$array->sortUStrings(UStr::COLLATION_DEFAULT, new ULoc("de_DE"));
// "ö", "z"
```

Enhanced Testing and Debugging
==============================

[](#enhanced-testing-and-debugging)

Phred takes the reliability of your applications exceptionally serious. Consideration was given to how bugs can be best detected, tracked, and reported.

The unit tests with which Phred is accompanied are just one part of the Phred's effort in making your projects maximally bug-free, dependable, and therefore secure. Aside from a few classes that are still in development or require intricate testing techniques that are yet to be implemented, practically every method of every class was thoroughly tested to make sure that it will work as intended and to achieve a high method and line coverage. The Phred's unit tests are powered by [PHPUnit](http://phpunit.de/) testing framework and can be run with `run-unit-tests.php` script located in the root directory. A byproduct of the unit tests is that they can serve as an abundant source of examples showing the majority of the Phred's classes and methods is use.

However, because unit tests are only run occasionally after a significant change has been introduced into the code and because the diversity of input values and their combinations are limited by how the tests were initially shaped, Phred deems unit tests not sufficient enough. For the purpose of keeping the chances of bugs getting into your applications as low as physically possible and in order to improve bug detection and bug tracking, Phred made a move to expand testing further on the runtime by means of semantic checks.

Semantic Checks
---------------

[](#semantic-checks)

If you look into one or more of the unit testing classes, you would see calls to `assertTrue` and `assertFalse` methods being made by every test. These assertions belong to PHPUnit framework and are the most common ways to verify if the results produced by a tested method appear to be correct. In a more general meaning, an assertion is a semantic check that determines whether or not a value or a mixture of values makes sense in a given context.

In PHP as well as in C/C++, Java, and a good number other languages, assertions is a built-in feature. They have proved to be very helpful in saving lots of time that otherwise would be spent on bug researching and allowed developers to come up with fixes quickly and with minimum negative effect on users. A downside of assertions is that they may considerably influence performance in dynamic languages like PHP. But what's great about assertions in PHP is that they can be easily enabled or disabled. If disabled, the PHP runtime skips over all assertions when executing a PHP script as if the assertions did not exist.

Semantic checks in Phred are ubiquitous and are embedded in almost every method as `assert` functions. Diligently playing their roles of safeguards against any misuse of methods and their parameters, the Phred's semantic checks is another obstacle for bugs to make their way into your application and remain undetected ever since. What assertions do in Phred when enabled is similar to what assertions do in unit tests with the key difference being that, in unit tests, assertions are focused on incorrect output while the primary focus of assertions in Phred's methods is incorrect input.

For the simple reason that incorrect input always leads to incorrect output, the Phred's semantic checks realize the previously unrealized side of the PHP's potential for bug detection, effectively making a Phred application with enabled assertions run as a unit test suite fed with highly diverse input that is generated by manifold user requests coming in great quantities.

Bug Tracking and Reporting
--------------------------

[](#bug-tracking-and-reporting)

The control panel for debugging and for configuring Phred's semantic checks can be found in `Application/Configuration/Debug.json`. And in `Application/Configuration/Admin.json` you can tell Phred the admin's email address to which error reports should be sent if mailing is enabled.

With the options that are offered to you in `Debug.json`, you can enable/disable the Phred's semantic checks with `enableAssertions` option, enable logging to a file about any encountered errors with the `logging` group of options, and enable mailing to the admin about errors with the `mailing` group of options. `Debug.json` also lets you configure the semantic checks so that they can be used on a production server with little to none impact on performance. You can tell Phred to keep the semantic checks enabled during an hour range, during one or more days of week, or during those days of year that are multiple of a certain number.

Of course, the PHP runtime can encounter errors on its own when, for example, you make a typo in the name of a method that is only called under particular circumstances or when you forget a parameter in one of such methods. If logging or mailing is enabled, Phred will be reporting about all kinds of errors and not just about errors raised by semantic checks. But for an error that was encountered by a semantic check, Phred is able to provide you with more of the valuable information that you would need for successful debugging. In addition to the usual information on the line number of an error that was snatched by a semantic check, Phred will also report:

1. The available backtrace of the error, appearing the chronological order.
2. The values of the method's parameters at the time of the error.
3. The values of the variables that were defined at the time.
4. The values of the object's properties at the time, unless the method is static.

If you are the admin and you have Phred configured to send a mail message to your email address about any detected error, a received message would look like this:

```
Something curious was encountered on hostname (12.34.56.78) in 'doc-root-dir'

[Time:] 2014-08-11 02:32:41 UTC

[Location:] PhredParty/Classes/String/CUString.php, line 1476

Assertion "!isset($length) || ($length >= 0 &&
$startPos + $length = 0 &&
$startPos + $length  ...",
    "text/html");
$mail->addCc("john.q.public@elpmaxe.com");
$mail->send();
```

Requesting
----------

[](#requesting)

The [CInetRequest](http://htmlpreview.github.com/?https://github.com/nazariyg/Phred/blob/master/doc/classes/CInetRequest.html) class (alias `InetReq`) lets you make HTTP, HTTPS, FTP, and FTPS requests over the Internet, as a part of a cookies-enabled session if used together with the [CInetSession](http://htmlpreview.github.com/?https://github.com/nazariyg/Phred/blob/master/doc/classes/CInetSession.html) class (alias `InetSess`).

For the HTTP and HTTPS protocols, the fully supported methods are GET, POST, PUT, DELETE, HEAD, and LIST. With HTTP/HTTPS, you can upload a file to a remote location via POST method (this flavor of POST method is referred to as `HTTP_UPLOAD`) or via PUT method. Aside from other purposes, the PUT and DELETE methods can be used for communication with RESTful APIs.

As an example, let's send an HTTP GET request to some web-based API, asking for the description of an item:

```
$req = new InetReq("http://example.com/item/1234/desc");  // HTTP_GET is the default
$req->setRequestTimeout(15);
$res = $req->send($success);
echo $success;  // true if successful, false otherwise
echo $res;      // the response
```

Or, if the API looks into the HTTP headers of a received request to see if the requested description should appear in a specific language, we could use it to ask for the description of an item in German by specifying the language in an HTTP header:

```
$req = new InetReq("http://example.com/item/1234/desc");
$req->setRequestTimeout(15);
$req->addHeader("lang", "de");
$res = $req->send($success);
echo $success;  // true if successful, false otherwise
echo $res;      // the response
```

And if the API allows for it, we could update the description of an item by means of an HTTP POST request:

```
$req = new InetReq("http://example.com/item/1234", InetReq::HTTP_POST);
$req->setRequestTimeout(15);
$req->addPostField("desc", "Description ...");
$req->send($success);
echo $success;  // true if successful, false otherwise
```

For downloading a response to a file, `downloadFile` static method of the CInetRequest class provides the convenience of downloading responses over any protocol without the need for creating an object or specifying the request type:

```
$success = InetReq::downloadFile("http://example.com/?get=1",
    "/path/to/downloaded/file", 30);
echo $success;  // true if successful, false otherwise
```

When multiple requests need to be sent or when the destination server is expected to set cookies that would need to be sent back later with subsequent requests, you can use the CInetSession class to improve performance by letting the requests be processed in parallel, to enable full cookie support, or both. As much as it is allowed by the maximum number of concurrent requests, which is configurable, a request in a session does not have to wait for the previously sent request to complete in order to be sent out.

With the CInetSession class, a session is not limited just to the initially added requests and any number of subsequent requests can be queued into the session from a callback function that, if specified, is invoked by the session after a request is complete:

```
$sess = new InetSess();
$req = new InetReq("http://example.com/item/1234/desc");
$sess->addRequest($req);
$req = new InetReq("http://example.com/item/5678/desc");
$sess->addRequest($req, function ($success, $res, $req, $sess) {
    echo $success;  // true if successful, false otherwise
    echo $res;      // the response
    // ...
    // After processing the response, it appears that we would also need
    // the description of one more item.
    $anotherReq = new InetReq("http://example.com/item/9012/desc");
    $sess->addRequest($anotherReq, function ($success, $res, $req, $sess) {
        echo $success;  // true if successful, false otherwise
        echo $res;      // the response
        // Add more requests to the session if necessary.
        // ...
    });
});
$sess->start();
```

Backward Compatibility
======================

[](#backward-compatibility)

Any library, API, or any other third-party component is backward-compatible with Phred as long as it is installed via Composer, which is de facto the standard package manager for PHP. Even Facebook is recommending Composer for installing its [Facebook SDK](https://developers.facebook.com/docs/php/gettingstarted/).

From the perspective of any third-party component, every OOP string is just a regular PHP string without any memory overhead or use restrictions. A PHP's native array becomes an OOP array when a third-party component in any way outputs it and the PHP array's keys are sequential (0, 1, 2, ...) or, if the array's keys are non-sequential, it arrives as an OOP map, just like you would expect. And when you pass an OOP array or an OOP map to a third-party component, that library or API receives it as a plain PHP array in all cases, just like the third-party component would expect.

The Phred's backward compatibility does not only cover parameters in methods and functions, but also return values and values being output by means of parameters that are declared by reference in methods and functions of third-party components. Furthermore, the backward compatibility comes into play even when you get or set a public property of an object of a class that was brought in by a third-party component, whether or not the class is using `__get` or `__set` "magic" methods for property access.

###  Health Score

27

—

LowBetter than 49% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity24

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/6996416?v=4)[Nazarii Horpyniuk](/maintainers/nazariyg)[@nazariyg](https://github.com/nazariyg)

---

Top Contributors

[![nazariyg](https://avatars.githubusercontent.com/u/6996416?v=4)](https://github.com/nazariyg "nazariyg (88 commits)")

### Embed Badge

![Health badge](/badges/phred-phred/health.svg)

```
[![Health](https://phpackages.com/badges/phred-phred/health.svg)](https://phpackages.com/packages/phred-phred)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
