PHPackages                             ardabeyazoglu/php-retention - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. ardabeyazoglu/php-retention

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

ardabeyazoglu/php-retention
===========================

File retention policy computation

v1.0.4(2y ago)16MITPHPPHP &gt;=8.1

Since Jan 30Pushed 2y ago1 watchersCompare

[ Source](https://github.com/ardabeyazoglu/php-retention)[ Packagist](https://packagist.org/packages/ardabeyazoglu/php-retention)[ RSS](/packages/ardabeyazoglu-php-retention/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (5)Dependencies (2)Versions (6)Used By (0)

php-retention
=============

[](#php-retention)

[![build](https://github.com/ardabeyazoglu/php-retention/actions/workflows/main.yml/badge.svg)](https://github.com/ardabeyazoglu/php-retention/actions/workflows/main.yml/badge.svg)[![codecov](https://camo.githubusercontent.com/8bb62ff01f1e00b5b1c8fc2799fd47409873b05f53cff45d6783297368e24d4e/68747470733a2f2f636f6465636f762e696f2f67682f61726461626579617a6f676c752f7068702d726574656e74696f6e2f67726170682f62616467652e7376673f746f6b656e3d355445324f4b61495054)](https://codecov.io/gh/ardabeyazoglu/php-retention)

A simple but handy php library to apply retention policy to files before deleting, archiving or anything that is possible with a custom callback. A typical example would be backup archiving based on custom policies such as "keep last 7 daily, 2 weekly and 3 monthly backups".

Features
========

[](#features)

- Apply hourly, daily, weekly, monthly and yearly policies
- Customize prune action to do something else instead of deleting (e.g. move them to cloud)
- Customize file finder logic (e.g. support different storage interfaces such as S3)
- Grouping files (e.g. prune multiple files together instead of a single file)
- Dry runnable
- Logger aware
- No dependencies

Install
=======

[](#install)

```
composer require ardabeyazoglu/php-retention

```

Test
====

[](#test)

```
composer test

```

Usage
=====

[](#usage)

```
// define retention policy (UTC timezone)
$retention = new PhpRetention\Retention([
    "keep-daily" => 7,
    "keep-weekly" => 4,
    "keep-monthly" => 6,
    "keep-yearly" => 2
]);

// customize finder logic if required
$retention->setFindHandler(function () {});

// customize time calculation if required
$retention->setTimeHandler(function () {});

// customize time calculation if required
$retention->setPruneHandler(function () {});

// apply retention in given directory (this WILL PRUNE the files!)
$result = $retention->apply("/path/to/files");
print_r($result);
```

Policy Configuration
====================

[](#policy-configuration)

This library is inspired by [Restic's policy model](https://restic.readthedocs.io/en/latest/060_forget.html#removing-snapshots-according-to-a-policy). Policy configuration without understanding how it works might be misleading. Please read the [explanation](https://restic.readthedocs.io/en/latest/060_forget.html#removing-snapshots-according-to-a-policy) to understand how each `keep-***` parameter works.

```
keep-last: keep the most recent N files. (default: 1)
keep-hourly: for the last N hours which have one or more files, keep only the most recent one for each hour.
keep-daily: for the last N days which have one or more files, keep only the most recent one for each day.
keep-weekly: for the last N weeks which have one or more files, keep only the most recent one for each week.
keep-monthly: for the last N months which have one or more files, keep only the most recent one for each month.
keep-yearly: for the last N years which have one or more files, keep only the most recent one for each year.

```

Examples
========

[](#examples)

### 1. Custom Finder

[](#1-custom-finder)

By default, it will scan target directory non-recursively and apply retention policy to each file or directory in the target directory. This will be okay for local files, mounted partitions and when using stream wrappers external storage protocols. In all other cases, a custom finder logic must be implemented to detect files to apply retention.

```
// get list of files from ftp
$rets->setFindHandler(function (string $targetDir) use ($ftpConnection) {
    $files = [];
    $fileList = ftp_mlsd($ftpConnection, $targetDir) ?: [];
    foreach ($fileList as $file) {
        $filename = $file['name'];
        $time = (int) $file['modify'];

        if (preg_match('/^backup_\w+\.zip$/', $filename)) {
            $date = new DateTimeImmutable('now', new DateTimeZone('UTC'));
            $date = $date->setTimestamp($time);

            $filepath = "$targetDir/$filename";

            $files[] = new FileInfo(
                date: $date,
                path: $filepath,
                isDirectory: false
            );
        }
    }

    return $files;
});
```

### 2. Custom Time Parser

[](#2-custom-time-parser)

By default, it will check posix modification time of files to detect date and time information. It can be overriden by using a custom function, to resolve a different date value. Can be handy for reading date information from a custom file name format.

NOTE: This will be only called when using default finder logic. If you are writing your own `findHandler`, that function is responsible for giving a valid list of files with a valid date info.

```
// assume the files waiting for retention have this format: "backup@YYYYmmdd.zip"
$ret->setTimeHandler(function (string $filepath, bool $isDirectory) {
    if (preg_match('/^backup@([0-9]{4})([0-9]{2})([0-9]{2})\.zip$/', basename($filepath), $matches)) {
        $year = intval($matches[1]);
        $month = intval($matches[2]);
        $day = intval($matches[3]);

        $date = new DateTimeImmutable('now', new DateTimeZone('UTC'));
        $date = $date->setDate($year, $month, $day)->setTime(0, 0, 0, 0);

        return new FileInfo(
            date: $date,
            path: $filepath,
            isDirectory: $isDirectory
        );
    }
    else {
        return null;
    }
});
```

### 3. Custom Prune Action

[](#3-custom-prune-action)

Pruning action is by default `unlink` for files and `rmdir` for empty directories. It can be customized to call different delete functions or to do something else other than deleting.

```
$ret->setPruneHandler(function (FileInfo $fileInfo) use ($ftpConnection) {
    ftp_delete($ftpConnection, $fileInfo->path);
});
```

### 4. Grouping Files to Prune Together

[](#4-grouping-files-to-prune-together)

Let's assume the directory contains multiple backup files for the same date but for different purposes. They can be grouped by using regexp to apply the same retention to altogether.

```
# example:
/backup/tenant/mysql-20240106.tar.gz
/backup/tenant/files-20240106.tar.gz
/backup/tenant/mysql-20240107.tar.gz
/backup/tenant/files-20240107.tar.gz

```

Without the following function, a "keep-last=1" policy will prune only the first file. By grouping them based on date, it will prune first two files together.

```
// group different types of backups based on tenant and date and prune them together
$ret->setGroupHandler(function (string $filepath) {
    if (preg_match('/\-([0-9]{8})\.tar\.gz$/', $filepath, $matches)) {
        // group by date str
        return $matches[1];
    }

    return null;
});
```

Contribution
============

[](#contribution)

Feel free to post an issue if you encounter a bug or you want to implement a new feature. Please be descriptive in your posts.

ToDo
====

[](#todo)

- Add keep-within-\*\*\* policy support

###  Health Score

24

—

LowBetter than 32% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity54

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.

###  Release Activity

Cadence

Every ~2 days

Total

5

Last Release

829d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f5c6c17d31b1cf3eb1a741ebfbcf2ff4103b9bacc89f3397c14c64cf2af65b81?d=identicon)[ardabeyazoglu](/maintainers/ardabeyazoglu)

---

Top Contributors

[![ardabeyazoglu](https://avatars.githubusercontent.com/u/1159836?v=4)](https://github.com/ardabeyazoglu "ardabeyazoglu (29 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ardabeyazoglu-php-retention/health.svg)

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

###  Alternatives

[google/cloud-core

Google Cloud PHP shared dependency, providing functionality useful to all components.

343121.4M79](/packages/google-cloud-core)[uspdev/replicado

Classes PHP que consome dados do Replicado USP

136.2k7](/packages/uspdev-replicado)

PHPackages © 2026

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