PHPackages                             equit/xray - 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. [Testing &amp; Quality](/categories/testing)
4. /
5. equit/xray

ActiveLibrary[Testing &amp; Quality](/categories/testing)

equit/xray
==========

A PHP testing utility for accessing protected and private class members

08284PHPCI failing

Since Feb 5Pushed 3mo ago1 watchersCompare

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

READMEChangelogDependenciesVersions (1)Used By (4)

XRay
====

[](#xray)

A PHP testing utility for accessing protected and private class members.

Sometimes in tests you need to access protected or private class members. PHP provides a Reflection API for this. XRay provides a convenience layer on top of this to make it easier to use, and the code using it easier to understand. The guiding principle is to attempt to make accessing inaccessible members the same as if the class were declared with those members `public`.

It comes in two flavours, one for instance members, the other for static members. The principles of the two are the same:

- Create an `XRay` (or `StaticXRay`) for the object (or class) under test
- Access the protected or private member as if it were a member of the (Static)XRay

Calling methods
---------------

[](#calling-methods)

The most common use-case is to unit test otheriwse inaccessible methods.

For instance methods, given the class

```
class TestThis
{
    protected function testMethod(): void
    {
        // ... do something testable
    }

    private function otherTestMethod(): void
    {
        // ... do something else testable
    }
}
```

You can call protected or private methods like this:

```
$objectUnderTest = new TestThis();
$xray = new XRay($objectUnderTest);
$xray->testMethod();
$xray->otherTestMethod();
```

You probably want to set some test expectations on your object under test; but if you don't need to set any, you can call its constructor directly in the `XRay` constructor:

```
$xray = new XRay(new TestThis());
```

For static methods it's similar, except you pass the fully-qualified class name to the `StaticXRay` constructor as a string, rather than an instance of the class:

```
class TestThis
{
    protected static function staticTestMethod(): void
    {
        // ... do something testable
    }

    protected static function otherStaticTestMethod(): void
    {
        // ... do something else testable
    }
}

$xray = new StaticXRay(TestThis::class);
$xray->staticTestMethod();
$xray->otherStaticTestMethod();
```

Note that you call static methods with a `StaticXRay` *as if they were instance methods*.

### Passing arguments

[](#passing-arguments)

Pass arguments exactly as you would if you were calling the method directly:

```
$xray->testMethod($var, "literal", new OtherObject());
```

Methods that accept arguments by reference, methods with optional arguments and methods that accept variable argument lists and/or parameter packs are fully supported.

### Return values

[](#return-values)

Receving return values is similarly unaltered:

```
$returnValue = $xray->testMethod(...$args);
```

Methods that return `void` will return `null` when invoked via an `XRay` (or `StaticXRay`). This is what PHP itself does, but you may find your IDE won't warn you as it would if you tried to do something with a `void` return directly.

Calling xrayed methods that return `never` will not return, as expected.

Accessing properties
--------------------

[](#accessing-properties)

Less commonly, you may want to access inaccessible class properties. As with calling methods, you can just treat your `XRay` as if it were an the xrayed object itself when accessing properties:

```
class TestThis
{
    private string $value;
}

$xray = new XRay(new TestThis());
$theValue = $xray->value;
```

Static properties can be accessed too:

```
class TestThis
{
    private static string $staticValue;
}

$xray = new StaticXRay(TestThis::class);
$theValue = $xray->staticValue;
```

### Setting property values

[](#setting-property-values)

If you need to you can set the values for protected and private properties:

```
class TestThis
{
    private string $value;
}

$xray = new XRay(new TestThis());
$xray->value = "the test value";
```

As elsewhere, this works with static properties using StaticXRay:

```
class TestThis
{
    private static string $staticValue;
}

$xray = new StaticXRay(TestThis::class);
$xray->staticValue = "the test value";
```

The only caveat to this is when you want to manipulate the content of array properties:

```
class TestThis
{
    private array $arrayValue;
}

$xray = new XRay(new TestThis());
$xray->arrayValue["test-key"] = "test-value";
```

Contrary to what you might expect, this does **not** set the value of the `"test-key"` key in the `$arrayValue` property of the object under test. What it does is set the value of that key on a local copy of the array. This is because when the code executes, `$xray->arrayValue` is a fetch of a copy of the property, to which `["test-key"] = "test-value"` adds the key and value. The local copy of the array is then immediately discarded since it's not assigned to anything. The property in the xrayed object remains unmodified throughout.

### Updating array properties

[](#updating-array-properties)

The way to set (or unset) a key on an array property is this:

```
class TestThis
{
    private array $arrayValue;
}

$xray = new XRay(new TestThis());
$temporaryArray = $xray->arrayValue["test-key"];
$temporaryArray["test-key"] = "test-value";
$xray->arrayValue = $temporaryArray;
```

It works this way for static array properties also:

```
class TestThis
{
    private static array $staticArrayValue;
}

$xray = new StaticXRay(TestThis::class);
$temporaryArray = $xray->staticArrayValue["test-key"];
$temporaryArray["test-key"] = "test-value";
$xray->staticArrayValue = $temporaryArray;
```

While this is more verbose than the rest of the library, and doesn't align with the principle of "an XRay behaves the same as the object it xrays", it's still clearer and more concise than using PHP's Reflection API directly.

Inherited members
-----------------

[](#inherited-members)

XRays work with members that the xrayed object or class inherits from base classes, as well as its own members. This is regardless of how far back up the inheritance tree the target method is. The syntax for accessing inherited members is no different from how you access the object or class's own members:

```
class BaseTestThis
{
    protected function doSomething(): void
    {
        // ... do something testable
    }
}

class TestThis extends BaseTestThis
{
    protected function doSomethingElse(): void
    {
        // ... do something else testable
    }
}

$xray = new XRay(new TestThis());
$xray->doSomething();
$xray->doSomethingElse();
```

If the object under test reimplements the inherited method, the reimplementation is the one that's called (just as it would be if it were a regular PHP method call):

```
class BaseTestThis
{
    protected function doSomething(): void
    {
        // ... the xray does not call this method
    }
}

class TestThis extends BaseTestThis
{
    protected function doSomething(): void
    {
        // ... this method gets called by the xray
    }
}

$xray = new XRay(new TestThis());
$xray->doSomething();
```

Inherited protected properties are supported too:

```
class BaseTestThis
{
    protected string $baseValue;
}

class TestThis extends BaseTestThis
{
}

$xray = new XRay(new TestThis());
$xray->baseValue = "test-value";
$theValue = $xray->baseValue;
```

### Base class private members

[](#base-class-private-members)

`XRay` and `StaticXRay` will permit access to private properties of ancestors of the xrayed object or class. Such members are not inherited by the xrayed object/class itself, and therefore following the principle of ***as if the class were declared with `protected`/`private` members `public`***, they ought not to be made visible in the xray. The reason they are visible is that the XRay applies the principle to the full inheritance hierarchy of the xrayed class/object. This facilitates setting test expectations when performing some operation on the object/class under test is expected to result in a state change in an ancestor class.

This applies only to properties - `XRay`s and `StaticXRay`s don't make private methods of base classes accessible. This is because the main purpose of xraying is to enable testing of inaccessible members where necessary. Private methods are not inherited, and don't need testing with the inheriting class. Therefore there's no need for an xray to make it possible to invoke them.

Public members
--------------

[](#public-members)

You can use `XRay` and `StaticXRay` objects to access `public` members just as you would `protected` and `private` members, and just as you would on the xrayed object itself (subject to the array member property caveat above). Doing so is entirely optional: if you'd prefer to access public members directly on the original object, it won't cause any problems for the XRay; if you'd prefer to use the `XRay` because it's more readable in your view, that's also fine.

Non-test uses
-------------

[](#non-test-uses)

Using `XRay` or `StaticXRay` for anything other than testing is not recommended. I've not yet come across any other use-case for which this type of approach is appropriate.

###  Health Score

24

—

LowBetter than 32% of packages

Maintenance54

Moderate activity, may be stable

Popularity17

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity12

Early-stage or recently created project

 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.

### Community

Maintainers

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

---

Top Contributors

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

### Embed Badge

![Health badge](/badges/equit-xray/health.svg)

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

###  Alternatives

[phpspec/prophecy

Highly opinionated mocking framework for PHP 5.3+

8.5k551.7M682](/packages/phpspec-prophecy)[vimeo/psalm

A static analysis tool for finding errors in PHP applications

5.8k77.5M6.7k](/packages/vimeo-psalm)[brianium/paratest

Parallel testing for PHP

2.5k118.8M754](/packages/brianium-paratest)[beberlei/assert

Thin assertion library for input validation in business models.

2.4k96.9M570](/packages/beberlei-assert)[mikey179/vfsstream

Virtual file system to mock the real file system in unit tests.

1.4k108.0M2.7k](/packages/mikey179-vfsstream)[orchestra/testbench

Laravel Testing Helper for Packages Development

2.2k39.1M32.1k](/packages/orchestra-testbench)

PHPackages © 2026

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