phplyr

Reference

Strings and dates

phplyr re-exports phibble’s stringr and lubridate functions. They build expressions, so they work in memory and, where a SQL translation exists, on database tables. Their results are checked against stringr 1.6.0 and lubridate 1.9.5.

use function Phplyr\{floor_date, str_detect, str_extract, str_replace_all, str_to_title, ymd};

$notes = DataFrame::fromRows([
    ['when' => '2024-01-31', 'note' => 'paid INV-1042 late'],
    ['when' => '2024-02-29', 'note' => 'refund inv-77'],
    ['when' => '2024-03-15', 'note' => 'no invoice'],
]);

// Dates: parse, round, shift by a month (an invalid date gives NA), weekday.
echo $notes |>
    mutate(
        date: ymd('when'),
        month_start: floor_date(ymd('when'), 'month'),
        next_month: ymd('when')->add(months(1)),
        weekday: wday(ymd('when'), weekStart: 1),
    ) |>
    select(except('note')), "\n";

// Strings: patterns are regular expressions unless wrapped in fixed().
echo $notes |>
    mutate(
        invoice: str_extract('note', regex('inv-\d+', ignoreCase: true)),
        literal: str_detect('note', fixed('INV')),
        masked: str_replace_all('note', '\d', '#'),
        title: str_to_title('note'),
    ) |>
    select(except('when', 'note'));
use function Phplyr\{floor_date, str_detect, str_extract, str_replace_all, str_to_title, ymd};

$notes = DataFrame::fromRows([
    ['when' => '2024-01-31', 'note' => 'paid INV-1042 late'],
    ['when' => '2024-02-29', 'note' => 'refund inv-77'],
    ['when' => '2024-03-15', 'note' => 'no invoice'],
]);

// Dates: parse, round, shift by a month (an invalid date gives NA), weekday.
echo phplyr($notes)
    ->mutate(
        date: ymd('when'),
        month_start: floor_date(ymd('when'), 'month'),
        next_month: ymd('when')->add(months(1)),
        weekday: wday(ymd('when'), weekStart: 1),
    )
    ->select(except('note')), "\n";

// Strings: patterns are regular expressions unless wrapped in fixed().
echo phplyr($notes)
    ->mutate(
        invoice: str_extract('note', regex('inv-\d+', ignoreCase: true)),
        literal: str_detect('note', fixed('INV')),
        masked: str_replace_all('note', '\d', '#'),
        title: str_to_title('note'),
    )
    ->select(except('when', 'note'));
# A tibble: 3 × 5
  when       date       month_start next_month weekday
  <chr>      <date>     <date>      <date>       <int>
1 2024-01-31 2024-01-31 2024-01-01  NA               3
2 2024-02-29 2024-02-29 2024-02-01  2024-03-29       4
3 2024-03-15 2024-03-15 2024-03-01  2024-04-15       5
# A tibble: 3 × 4
  invoice  literal masked             title             
  <chr>    <lgl>   <chr>              <chr>             
1 INV-1042 TRUE    paid INV-#### late Paid Inv-1042 Late
2 inv-77   FALSE   refund inv-##      Refund Inv-77     
3 <NA>     FALSE   no invoice         No Invoice

Strings

As in stringr, a pattern is a regular expression. fixed('a.b') matches the text literally, and regex('ab', ignoreCase: true) sets options.

Function Value
str_detect(x, p, negate:), str_starts, str_ends Whether the pattern matches
str_count(x, p) Number of matches
str_extract(x, p) The first match, or NA
str_extract_all(x, p), str_split(x, p) A list per row (a list-column)
str_split_i(x, p, i) The i-th piece; negative counts from the end
str_replace(x, p, r), str_replace_all Replace the first or every match; \1 refers to a group
str_pad(x, width, side:, pad:) Pad to a width
str_trunc(x, width, side:, ellipsis:) Truncate with an ellipsis
str_to_title, str_to_upper, str_to_lower Change case
str_squish, str_trim(x, side) Remove whitespace
str_length, str_sub(x, start, end) Length and substrings

Strings are UTF-8. str_length(), str_sub(), str_pad(), and the other functions count UTF-8 characters, and the case functions change accented letters too.

str_replace, str_pad, and str_split have the names of PHP built-ins. In a file that imports them, write \str_replace() for the built-in.

Dates and times

Function Value
ymd, dmy, mdy, ymd_hms, dmy_hms, mdy_hms (tz:) Parse text; failures give NA
parse_date_time(x, ['Ymd HMS', 'dmY HMS']) Parse with lubridate orders
year, month, day, yday, quarter, hour, minute, second Parts
wday(x, weekStart:) Day of the week; Sunday is 1 unless weekStart: 1
floor_date(x, unit), ceiling_date, round_date Round to 'hour', '15 minutes', 'week', 'month', and so on
seconds(n) … years(n) Periods: col('d')->add(months(1))
difftime(x, y, units:) The difference as a number
with_tz(x, tz), force_tz(x, tz) Change the displayed zone, or the zone itself
today(tz:), now(tz:) The current date or time

The parsing functions return UTC date-times unless tz: names a zone, as in lubridate. PHP’s default time zone does not change the result. with_tz() keeps the instant and changes the zone it is shown in, and force_tz() keeps the clock time and changes the zone.

On a database table, a function without a SQL translation for that database loads the rows and runs in PHP, as Databases and expressions describes.

Month and year arithmetic that lands on a date that doesn’t exist, such as January 31 plus one month, gives NA, as in lubridate. Subtracting dates gives days, and subtracting datetimes gives seconds. Date columns stay dates through these functions. Phibble\Expr\Clock::freeze() fixes today() and now() in tests.

From phpstats/phplyr@d319dc5.