ReadMe.md
<p align="center">
<a href="https://t-regx.com"><img src="t.regx.png" alt="T-Regx"></a>
</p>
<p align="center">
<a href="https://github.com/t-regx/T-Regx/actions/"><img src="https://github.com/t-regx/T-Regx/workflows/build/badge.svg" alt="Build status"/></a>
<a href="https://coveralls.io/github/t-regx/T-Regx"><img src="https://coveralls.io/repos/github/t-regx/T-Regx/badge.svg" alt="Unit tests"/></a>
<a href="https://github.com/t-regx/T-Regx/releases"><img src="https://img.shields.io/badge/latest-0.41.5-brightgreen.svg?style=popout" alt="latest: 0.41.5"/></a>
<a href="https://github.com/t-regx/T-Regx"><img src="https://img.shields.io/badge/dependencies-0-brightgreen.svg" alt="dependencies: 0"/></a>
</p>
# T-Regx | Regular Expressions library
PHP regular expressions brought up to modern standards.
[See documentation](https://t-regx.com/) at [t-regx.com](https://t-regx.com/).
[![last commit](https://img.shields.io/github/last-commit/t-regx/T-Regx/develop.svg)](https://github.com/t-regx/T-Regx/commits/develop)
[![commit activity](https://img.shields.io/github/commit-activity/y/t-regx/T-Regx.svg)](https://github.com/t-regx/T-Regx)
[![Commits since](https://img.shields.io/github/commits-since/t-regx/T-Regx/0.41.5/develop.svg)](https://github.com/t-regx/T-Regx/compare/0.41.5...develop)
[![Unit tests](https://img.shields.io/badge/Unit%20tests-3867-brightgreen.svg)](https://github.com/t-regx/T-Regx)
[![Unit tests](https://img.shields.io/badge/Assertions-6079-brightgreen.svg)](https://github.com/t-regx/T-Regx)
[![Code Climate](https://img.shields.io/codeclimate/maintainability/t-regx/T-Regx.svg)](https://codeclimate.com/github/t-regx/T-Regx)
[![FQN](https://img.shields.io/badge/FQN-used-blue.svg)](https://github.com/kelunik/fqn-check)
[![PRs Welcome](https://img.shields.io/badge/PR-welcome-brightgreen.svg?style=popout)](http://makeapullrequest.com)
[![Gitter](https://badges.gitter.im/T-Regx/community.svg)](https://gitter.im/T-Regx/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![OS Arch](https://img.shields.io/badge/OS-32‐bit-brightgreen.svg)](https://github.com/t-regx/T-Regx/actions)
[![OS Arch](https://img.shields.io/badge/OS-64‐bit-brightgreen.svg)](https://github.com/t-regx/T-Regx/actions)
[![OS Arch](https://img.shields.io/badge/OS-Windows-blue.svg)](https://github.com/t-regx/T-Regx/actions)
[![OS Arch](https://img.shields.io/badge/OS-Linux/Unix-blue.svg)](https://github.com/t-regx/T-Regx/actions)
[![PHP Version](https://img.shields.io/badge/PHP-7.1-blue.svg)](https://github.com/t-regx/T-Regx/actions)
[![PHP Version](https://img.shields.io/badge/PHP-7.2-blue.svg)](https://github.com/t-regx/T-Regx/actions)
[![PHP Version](https://img.shields.io/badge/PHP-7.3-blue.svg)](https://github.com/t-regx/T-Regx/actions)
[![PHP Version](https://img.shields.io/badge/PHP-7.4-blue.svg)](https://github.com/t-regx/T-Regx/actions)
[![PHP Version](https://img.shields.io/badge/PHP-8.0-blue.svg)](https://github.com/t-regx/T-Regx/actions)
[![PHP Version](https://img.shields.io/badge/PHP-8.1-blue.svg)](https://github.com/t-regx/T-Regx/actions)
[![PHP Version](https://img.shields.io/badge/PHP-8.2-blue.svg)](https://github.com/t-regx/T-Regx/actions)
1. [Installation](#installation)
* [Composer](#installation)
2. [Examples](#examples)
3. [Documentation](#documentation)
4. [T-Regx fiddle - Try online](#try-it-online-in-your-browser)
5. [API](#api)
1. [For standard projects -`pattern()`](#written-with-clean-api)
2. For legacy projects - `preg::match_all()`
6. [Overview](#why-t-regx-stands-out)
1. [Prepared patterns](#prepared-patterns)
2. [Working with the developer](#working-with-the-developer)
3. [Clean API](#written-with-clean-api)
4. [Fatal errors](#protects-you-from-fatal-errors)
5. [Clean Code](#t-regx-follows-the-philosophy-of-uncle-bob-and-clean-code)
6. [Exceptions vs. errors](#exceptions-over-warningserrors)
7. [Comparison](#comparison)
1. [Exceptions over warnings/errors](#exceptions-over-warningserrors)
2. [Working with the developer](#working-with-the-developer)
3. [Written with clean API in mind](#written-with-clean-api)
4. [Philosophy of Uncle Bob and "Clean Code"](#t-regx-follows-the-philosophy-of-uncle-bob-and-clean-code)
8. [Plans for the future](#current-work-in-progress)
9. [Sponsors](#sponsors)
10. [License](#license)
[Buy me a coffee!](https://www.buymeacoffee.com/danielwilkowski)
# Installation
Installation for PHP 7.1 and later (PHP 8 as well):
```bash
composer require rawr/t-regx
```
T-Regx only requires `mb-string` extension. No additional dependencies or extensions are required.
# Examples
Illustration of methods `match()`, `test()` and `count()`.
```php
$pattern = Pattern::of("ups"); // pattern("ups") also works
$matcher = $pattern->match('yay, ups');
foreach ($matcher as $detail) {
$detail->text(); // (string) "ups";
$detail->offset(); // (int) 0
}
if (!$matcher->test()) {
echo "No occurrances found";
} else {
echo "Found {$matcher->count()} occurrences";
}
```
# Documentation
Full API documentation is available at [t-regx.com](https://t-regx.com/). List of changes is available
in [ChangeLog.md](https://github.com/t-regx/T-Regx/blob/develop/ChangeLog.md).
Quick links:
- [Introduction](https://t-regx.com/docs/introduction) - https://t-regx.com/docs/introduction
- [Matching overview](https://t-regx.com/docs/match) - https://t-regx.com/docs/match
- [Replacing](https://t-regx.com/docs/replace) - https://t-regx.com/docs/replace
- [Splitting](https://t-regx.com/docs/split) - https://t-regx.com/docs/split
- [Prepared patterns](https://t-regx.com/docs/prepared-patterns) - https://t-regx.com/docs/prepared-patterns
- [Template syntax](https://t-regx.com/docs/template-syntax) - https://t-regx.com/docs/template-syntax
- [Pattern list](https://t-regx.com/docs/pattern-list) - https://t-regx.com/docs/pattern-list
# Try it online, in your browser!
Open [T-Regx fiddle](https://repl.it/github/T-Regx/fiddle) and start playing around right in your browser.
[Try now!](https://repl.it/github/T-Regx/fiddle)
# API
Choose the interface:
- I choose the **modern regex API**:
[Scroll to see](#written-with-clean-api) - `pattern()->test()`, `pattern()->match()`, `pattern()->replace()`
- I choose to **keep PHP methods** *(but protected from errors/warnings)*:
[Scroll to see](#exceptions-over-warningserrors) - `preg::match_all()`, `preg::replace_callback()`, `preg::split()`
For standard projects, we suggest `pattern()`. For legacy projects, we suggest `preg::match_all()`.
- Standard T-Regx
```php
$pattern = Pattern::of("ups"); // pattern("ups") also works
$matcher = $pattern->match('yay, ups');
if (!$matcher->test()) {
echo "Unmatched subject :/";
}
foreach ($matcher as $detail) {
$detail->text(); // (string) "ups";
$detail->offset(); // (int) 0
}
$pattern->replace('well, ups')->with('heck') // (string) "well, heck";
```
- Legacy API
```php
try {
preg::match_all('/?ups/', 'ups', $match, PREG_PATTERN_ORDER);
echo $match[0][0];
} catch (\TRegx\Exception\MalformedPatternException $exception) {
echo "Invalid pattern";
}
```
# Why T-Regx stands out?
:bulb: [See documentation at t-regx.com](https://t-regx.com/)
* ### Prepared patterns
Using user data isn't always safe with PCRE (even with `preg_quote()`), as well as just not being that
convenient to use. T-Regx provides dedicated solution for building patterns with unsafe user input.
Choose `Pattern::inject()` for simply including user data as literals. Use `Pattern::mask()` to convert user-supplied
masks into full-fledged patterns, safely. Use `Pattern::template()` for constructing more complex patterns.
```php
function makePattern($name): Pattern {
if ($name === null) {
return Pattern::of("name[:=]empty");
}
return Pattern::inject("name[:=]@;", [$name]); // inject $name as @
}
$gibberish = "(my?name)";
$pattern = makePattern($gibberish);
$pattern->test('name=(my?name)'); // (bool) true
```
* ### Working **with** the developer
* Simple methods
* T-Regx exposes functionality by simple methods, which return `int`, `string`, `string[]` or `bool`, which
aren't nullable. If you wish to do something with your match or pattern, there's probably a method for that,
which does exactly and only that.
* Strings:
* [Fixing error with multibyte offset (utf-8 safe)](https://t-regx.com/docs/match-details#offsets).
* Separate methods for positions:
* `offset()` - which returns position of a match in characters in UTF-8
* `byteOffset()` - which returns position of a match in bytes, regardless of encoding
* Groups:
* When using `preg::match_all()`, we receive an array, of arrays, of arrays. In contrast, T-Regx
returns an array of groups: `Group[]`. Object `Group` contains all the information about the group.
* Group errors:
* When invalid group named is used `get('!@#')`, T-Regx throws `\InvalidArgumentException`.
* When attempt to read a missing group, T-Regx throws `NonexistentGroupException`.
* When reading a group that happens not to be matched, T-Regx throws `GroupNotMatchedException`.
* ### Written with clean API
* Descriptive, simple interface
* Unicode support out-of-the-box
* No Reflection used, `No (...varargs)`, `No (boolean arguments, true)`, `(No flags, 1)`, `[No [nested, [arrays]]]`
* Inconsistencies between PHP versions are eliminated in T-Regx
* ### Protects you from fatal errors
Certain arguments cause fatal errors with `preg_()` methods, which terminate the application and can't be caught.
T-Regx will predict if given argument would cause a fatal error, and will throw a catchable exception instead.
* ### T-Regx follows the philosophy of Uncle Bob and "Clean Code"
Function should do one thing, it should do it well. A function should do exactly what you expect it to do.
* ### Compatible with other tools and libraries
Granted, `Pattern::of()` accepts undelimited pattern (`(Foo){3,4}}`) is not suitable with other PHP libraries,
which work with delimited patterns (`/(Foo){3,4}/`), for example Laravel and Routing. For that case,
use `PcrePattern::of()` which accepts plain-old standard PHP syntax.
* ### Exceptions over warnings/errors
* Unlike PHP methods, T-Regx doesn't use warnings/notices/errors for unexpected inputs:
```php
try {
preg::match_all('/([a3]+[a3]+)+3/', 'aaaaaaaaaaaaaaaaaaaa 3');
} catch (\TRegx\SafeRegex\Exception\CatastrophicBacktrackingException $exception) {
// caught
}
```
* Detects **malformed patterns** in and throws `MalformedPatternException`. This is impossible to catch
with `preg_last_error()`.
```php
try {
preg::match('/?ups/', 'ups');
} catch (\TRegx\Exception\MalformedPatternException $exception) {
// caught
}
```
* Not every error in PHP can be read from `preg_last_error()`, however T-Regx throws dedicated exceptions for those
events.
# Comparison
![Ugly api](https://t-regx.com/img/external/readme/preg.png)
or
![Pretty api](https://t-regx.com/img/external/readme/t-regx.png?)
# Current work in progress
Current development priorities, regarding release of 1.0:
- Separate SafeRegex and CleanRegex into to two packages, so users can choose what they need #103
- Add documentation to each T-Regx public method #17 \[in progress]
- Revamp of [t-regx.com](https://t-regx.com/) documentation \[in progress]
- Release 1.0
# Sponsors
- [Andreas Leathley](https://github.com/iquito) - developing [SquirrelPHP](https://github.com/squirrelphp)
- [BarxizePL](https://github.com/BarxizePL) - Thanks!
# T-Regx is developed thanks to
<a href="https://www.jetbrains.com/?from=T-Regx">
<img src="https://t-regx.com/img/external/jetbrains-variant-4.svg" alt="JetBrains"/>
</a>
## License
T-Regx is [MIT licensed](LICENSE).