PHPackages                             macfja/php-kvo - 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. macfja/php-kvo

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

macfja/php-kvo
==============

The KVO (Key Value Observing) design pattern in PHP

v0.1.0(10y ago)114MITPHPPHP &gt;=5.4.0

Since Feb 20Pushed 10y ago1 watchersCompare

[ Source](https://github.com/MacFJA/PhpKVO)[ Packagist](https://packagist.org/packages/macfja/php-kvo)[ RSS](/packages/macfja-php-kvo/feed)WikiDiscussions master Synced 4w ago

READMEChangelogDependencies (6)Versions (2)Used By (0)

PhpKVO
======

[](#phpkvo)

### [What is KVO](#what) [Installation](#installation) [Usage](#usage) [API](#api)

[](#what-is-kvo--installation--usage--api)

What is KVO ?
-------------------------------------------

[](#what-is-kvo-)

KVO (*Key Value Observing*) is a design pattern which allows an object to get notified about changes.
It allow you keep your object synchronized each other without creating a hard link thanks to Subject/Observer design pattern.
KVC (*Key Value Coding*) and KVO (*Key Value Observing*) are heavily used in Cocoa Framework (Objective-C)

Installation
--------------------------------------------------

[](#installation)

The simplest way to install the library is to use [Composer](http://getcomposer.org/):

```
composer require macfja/php-kvo
```

Usage
------------------------------------

[](#usage)

```
class Downloader extends AbstractObservable
{
  protected $progress;
  public function getProgress()
  {
    return $this->progress;
  }
  protected function receiveCallback($newProgress)
  {
    $this->setValueForKey('progress', $newProgress);
    // ... do something with the data
  }
  public function download()
  {
    // ... start the download
  }
}

class ProgressDisplay implements Listener
{
  public function observeValueForKeyPath($keyPath, $object, $change, &$context)
  {
    if ($keyPath == 'progress') {
      echo sprintf('Download in progress (%d%%)%s', $change[Observer::CHANGE_NEW], PHP_EOL);
    }
  }
}

$downloader = new Downloader();
$progress = new ProgressDisplay();
$downloader->addObserverForKey($progress, 'progress', Observer::OPTION_NEW|Observer::OPTION_INITIAL);
$downloader->download()
```

---

A complete examples can be found in the directory [examples](examples).

API
--------------------------------

[](#api)

### API of `Observable` interface

[](#api-of-observable-interface)

Implemented in `AbstractObservable`, `Proxy`.
A trait for quick implementation is available: `ObservableTrait`

#### API of `Observable::addObserverForKey` method

[](#api-of-observableaddobserverforkey-method)

This method allow you to subscribe to key value changes notification.

TypeVariableDescription[`Listener`](#api_listener)`$listener`The object that subscribe to the changes notification.string`$key`The key to listen.int|`0``$options` *optional*The list of options. (More information below)mixed|`null``&$context` *optional*The context: data to send with the notification. This value passed by reference.#### API, The `Observable::addObserverForKey` options list.

[](#api-the-observableaddobserverforkey-options-list)

- **`Observer::OPTION_NEW`**, Indicates that the change array should provide the new attribute value, if applicable.
- **`Observer::OPTION_OLD`**, Indicates that the change array should contain the old attribute value, if applicable.
- **`Observer::OPTION_INITIAL`**, If specified, a notification should be sent to the observer immediately, before the observer registration method even returns.

    The change array in the notification will always contain an `Observer::CHANGE_NEW` entry if `Observer::OPTION_NEW` is also specified but will never contain an `Observer::CHANGE_OLD` or `Observer::CHANGE_REQUESTED` entry.
    (In an initial notification the current value of the observed property may be old, but it's new to the observer.)
- **`Observer::OPTION_PRIOR`**, Whether separate notifications should be sent to the observer before and after each change, instead of a single notification after the change.

    The change array in a notification sent before a change always contains an `Observer::CHANGE_PRIOR` entry whose value is `true`, but never contains an `Observer::CHANGE_NEW` entry. When this option is specified the change array in a notification sent after a change contains the same entries that it would contain if this option were not specified. You can use this option when the observer's own key-value observing-compliance requires it to invoke the `willChangeValueForKey` method for one of its own properties, and the value of that property depends on the value of the observed object's property.

#### API of `Observable::willChangeValueForKey` method

[](#api-of-observablewillchangevalueforkey-method)

This method trigger a notification for all [`Listener`](#api_listener) that registered for a key with the option `Observer::OPTION_PRIOR`.
This method should be call **before** the key value change.

TypeVariableDescriptionstring`$key`The key that is changed.string`$source`The source/type of change. (More information below).null|mixed`$oldValue` *optional*The current value of the key.null|mixed`$requestedValue` *optional*The requested new value of the key.#### API of `Observable::didChangeValueForKey` method

[](#api-of-observabledidchangevalueforkey-method)

This method trigger a notification for all [`Listener`](#api_listener) that registered for a key without the option `Observer::OPTION_PRIOR`.
This method should be call **after** the key value change.

TypeVariableDescriptionstring`$key`The key that is changed.string`$source`The source/type of change. (More information below).mixed|`null``$oldValue` *optional*The value of the key before the change.mixed|`null``$requestedValue` *optional*The requested new value of the key.mixed|`null``$newValue` *optional*The current value of the key.#### API of `Observer::setValueForKey` method

[](#api-of-observersetvalueforkey-method)

This method change the value of a key and handle the call of methods `Observable::willChangeValueForKey` and `Observable::didChangeValueForKey`.

TypeVariableDescriptionstring`$key`The key to change.mixed`$value`The new value.#### API, the `Observable::willChangeValueForKey` and `Observable::didChangeValueForKey` source value

[](#api-the-observablewillchangevalueforkey-and-observabledidchangevalueforkey-source-value)

- **`Observer::SOURCE_SETTER`**, If the value of the key was changed from a *setter* method. (Used by [`Proxy`](#api_proxy) class)
- **`Observer::SOURCE_PROPERTY`**, If the value of the key was changed from a direct property change (public class property). (Used by [`Proxy`](#api_proxy) class)
- **`Observer::SOURCE_CUSTOM`**, If the value was change without a setter, or from a direct property access.

Note: You can use your own source type, it's just a string.

Note: **`Observer::SOURCE_INITIAL`**, If the [`Listener`](#api_listener) has the option `Observer::OPTION_INITIAL`, then when registered, this source is used

### API of `Listener` interface

[](#api-of-listener-interface)

#### API of `Listener::observeValueForKeyPath` method

[](#api-of-listenerobservevalueforkeypath-method)

This method is call every time an observed key is modified.

TypeVariableDescriptionstring`$keyPath`The modified key.object`$key`The modified object.array`$change`The changed data. (More information below)mixed`&$context`The changed context (provided on subscription).#### API, The `Listener::observeValueForKeyPath` change array

[](#api-the-listenerobservevalueforkeypath-change-array)

- **`Observer::CHANGE_NEW`**, If the `Observer::OPTION_NEW` was specified when the observer was registered, the value of this key is the new value for the attribute.
- **`Observer::CHANGE_OLD`**, If the `Observer::OPTION_OLD` was specified when the observer was registered, the value of this key is the value before the attribute was changed.
- **`Observer::CHANGE_PRIOR`**, If the option `Observer::OPTION_PRIOR` was specified when the observer was registered this notification is sent prior to a change.

    The change array contains an `Observer::CHANGE_PRIOR` entry whose value is `true` if the nofication is before the change, or `false` if the notification is after.

###  Health Score

23

—

LowBetter than 26% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity7

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity48

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

Unknown

Total

1

Last Release

3782d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1475671?v=4)[MacFJA](/maintainers/MacFJA)[@MacFJA](https://github.com/MacFJA)

---

Top Contributors

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

---

Tags

design-patternkvodesign patternKVCKVOKey Value Observing

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/macfja-php-kvo/health.svg)

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

###  Alternatives

[league/pipeline

A plug and play pipeline implementation.

1.0k16.8M84](/packages/league-pipeline)

PHPackages © 2026

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