PHPackages                             vincenzoraco/again - 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. vincenzoraco/again

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

vincenzoraco/again
==================

A PHP package to follow best practices when using loops

v1.0.0(2mo ago)42MITPHPPHP &gt;=8.3CI passing

Since Nov 19Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/vincenzoraco/again)[ Packagist](https://packagist.org/packages/vincenzoraco/again)[ RSS](/packages/vincenzoraco-again/feed)WikiDiscussions main Synced today

READMEChangelog (2)Dependencies (9)Versions (3)Used By (0)

Again
=====

[](#again)

A handy utility package that lets you write loops in a declarative way and avoid infinite loops.

Requirements
------------

[](#requirements)

- PHP 8.3 or higher

Installing
----------

[](#installing)

```
composer require vincenzoraco/again
```

Usage
-----

[](#usage)

When you need to execute a block of code repeatedly until a specific condition is met, you might typically use a `while`loop. For example:

```
while (!$this->isSent()) {
    $this->sendEmail();
}
```

This approach works, but it can lead to an **infinite loop** if the condition is never met or the exit condition is not properly defined. To prevent this, it is essential to add safeguards, such as limiting the number of iterations.

### Introducing Again

[](#introducing-again)

**Again** simplifies repetitive task execution with built-in safeguards to prevent infinite loops. It allows you to specify both a maximum number of iterations and a stopping condition, ensuring your loop behaves as expected.

Here’s how **Again** can help:

```
Again::perform(fn(int $i) => $this->sendEmail())
  ->limitTo(3)                                    // Stop after 3 attempts
  ->until(fn(int $i) => !$this->isSent())         // Keep going while email is not sent
  ->execute();
```

In this example, the email sending will attempt up to **3 times** while `$this->isSent()` is `false`. If the email is sent before the limit, the loop stops early. If it isn’t sent after 3 attempts, the loop will automatically stop, preventing an endless loop.

### How `until()` works

[](#how-until-works)

The `until()` closure uses **"while" semantics**: return `true` to keep iterating, `false` to stop. Think of it as "keep going while this condition holds":

```
// Keep retrying while the job is still pending
->until(fn(int $i) => $this->job->isPending())

// Keep going while there are items left to process
->until(fn(int $i) => $this->hasItemsLeft())
```

### Example with Custom Logic

[](#example-with-custom-logic)

You can also define your loop behavior using more complex logic by passing custom conditions or limits. For example:

```
$action = Again::perform($actionLogic)
  ->limitTo($limit)  // Limit the number of iterations
  ->until($until)    // Keep going while condition returns true
  ->execute();
```

### Know Why the Loop Ended

[](#know-why-the-loop-ended)

Sometimes you need to know why the loop has ended, whether it reached the maximum number of iterations or the stop condition was met. **Again** provides the `AgainStopReason` enum to indicate the reason for the loop termination.

For example:

```
$stopReason = Again::perform($actionLogic)
  ->limitTo($limit)
  ->until($until)
  ->execute();  // Execute and get the stop reason

// Check the reason for stopping
if ($stopReason === AgainStopReason::MAX_ITERATIONS_REACHED) {
    $this->notifyToSlack(‘Maximum retries reached.’);
    // Handle the case when max iterations are reached
}

if ($stopReason === AgainStopReason::CONDITION_MET) {
    // Handle the case when the condition was met and the loop stopped
}
```

### Key Features:

[](#key-features)

- **Limit the number of iterations:** Use the `limitTo()` method to specify a maximum number of executions, preventing infinite loops.
- **Custom stop condition:** Use the `until()` method to define a condition that keeps the loop running while it returns `true`. The loop stops as soon as the condition returns `false`.
- **Flexible execution:** Pass custom logic or actions to be executed on each iteration.
- **Track loop termination reason:** The `execute()` method returns an `AgainStopReason` value, letting you know why the loop ended.

Contributing
------------

[](#contributing)

You can contribute in one of three ways:

1. File bug reports using the [issue tracker](https://github.com/vincenzoraco/again/issues).
2. Answer questions or fix bugs on the [issue tracker](https://github.com/vincenzoraco/again/issues).
3. Contribute new features or update the wiki.

*The code contribution process is not very formal. You just need to make sure that you follow the PER Coding Style (enforced by Laravel Pint). Any new code contributions must be accompanied by unit tests where applicable.*

License
-------

[](#license)

MIT

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance85

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity55

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 ~512 days

Total

2

Last Release

79d ago

Major Versions

v0.0.1 → v1.0.02026-04-16

PHP version history (2 changes)v0.0.1PHP &gt;=8.2

v1.0.0PHP &gt;=8.3

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/5623078?v=4)[Vincenzo Raco](/maintainers/vincenzoraco)[@vincenzoraco](https://github.com/vincenzoraco)

---

Top Contributors

[![vincenzoraco](https://avatars.githubusercontent.com/u/5623078?v=4)](https://github.com/vincenzoraco "vincenzoraco (25 commits)")

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[mirzabusatlic/laravel-schedule-monitor

Monitor the output of scheduled tasks in a database table

2872.5k](/packages/mirzabusatlic-laravel-schedule-monitor)[prinsfrank/measurement-unit

A collection of measurement units for easy conversion

119.5k](/packages/prinsfrank-measurement-unit)[tamkeen-tech/laravel-enum-state-machine

Control your state using enums

142.1k](/packages/tamkeen-tech-laravel-enum-state-machine)

PHPackages © 2026

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