PHPackages                             evo/ejinn - 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. evo/ejinn

ActiveLibrary

evo/ejinn
=========

eJinn the Exception genie

2.1.0(1y ago)046GPL-3.0PHPPHP &gt;=8.3

Since Feb 17Pushed 1y ago1 watchersCompare

[ Source](https://github.com/ArtisticPhoenix/eJinn)[ Packagist](https://packagist.org/packages/evo/ejinn)[ RSS](/packages/evo-ejinn/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (6)Dependencies (1)Versions (7)Used By (0)

eJinn v2
========

[](#ejinn-v2)

The Exception Genie (PHP v8.3+)
-------------------------------

[](#the-exception-genie-php-v83)

### Overview

[](#overview)

- Do you like having one exception code per exception class?
- Do you like using interfaces to group exception classes togather?
- Do you like having error codes that are unique in your whole library, or project?
- Are you tired of keeping track of all those pesky exception codes?
- Are you tired of creating those boring boilerplate exception and interface classes?
- Are you tired of searching for the approrate exception, because you can't remember if you made one for this or that error?

If you answered *Yes* to any of the above then **eJinn** may be just what you need. **eJinn** builds excpetion classes and interfaces based on a simple and flexable configuration file. **eJinn** allows you to orginize all your *exception classes* and *error codes* in a logically based and easy to read configuration.

**eJinn** builds *PHP* exception and interface class files for you. You do not need to upload **eJinn** to your live server, simply upload the exceptions it creates!

An example of an **eJinn** configuration ( as a *PHP* array).

```
    return [
        "author"        => "ArtisticPhoenix",
        "buildpath"     => ["psr"=>4],
        "description"   => "eJinn The Exception Genie",
        "package"       => "eJinn",
        "subpackage"    => "",
        "support"       => "https://github.com/ArtisticPhoenix/eJinn/issues",
        "version"       => "1.0.0",
        "namespaces"     => [
            //{key:Namespace} => [{value:Namespace tier}]
            "eJinn\\Exception"  => [
                "subpackage"    => "Exception",
                "interfaces"    => [
                     //{key:numeric} => {value:Base class name OR Interface Tier}
                    "eJinnExceptionInterface"
                ],
                "exceptions" => [
                    //{key:Error_Code} => {value:Base class name OR Excption Tier}
                    0     => "UnknownError",
                    1001  => "ResservedCode",
                    1002  => "UnknownConfKey",
                    1003  => "DuplateCode",
                    1004  => "MissingRequired",
                    1005  => "KeyNotAllowed",
                    1006  => "KeyNotAllowed",
                    1007  => "ConfCollision",
                    1100  => [
                        "name" => "JsonParseError",
                        "reserved" => [[1101,1108]], //reserve range [1101-1108] @see: (json_last_error() + 1100)
                    ]
                ]//end exceptions
            ]//end namespace "eJinn\Exception"
        ] //end namespaces
    ];//end config
```

**eJinn's** configuration is based on a *Multiple Tier* based tree with *Top Down Inheritance*. This lets us have the maximum flexabillity while still maintaining the minimum configuration size. Properties are passed down the tree from the higher level *(Parent)* to lower levels *(Children)*. If a property exists in both the *Child* and the *Parent*, then the value for the *Child's* property is used.

The configuration can be as simple as the *PHP* array shown above, or you can use an `\eJinn\Reader\{Type}` class to convert many diffrent types of files into the *PHP* array which is then passed into the `\eJinn\eJinnParser` class. The *Reader* is desined to be extendable, so if one doesn't exist for the type of configuration file you prefer, let us know!

Configuration Schema
--------------------

[](#configuration-schema)

Property matching in **eJinn** is case insensative, so the property named "version" will match ( but is not limited to ) any of the following: "VERSION", "Verison and "vErSiOn".

The main tiers of the configuration are *Global*, *Namespace* and *Entity (Interface or Excption)* . The *Global* tier is the top level of the configuration array. The *Namespace* is the array contained within the **namespaces** property. The *Interfaces* and *Exceptions* tiers contain the final definition for the Interface and Exception classes **eJinn** will create. For the most part we will refer to them collectivly as the *Entity* tier. They share many simularities, however there are some signifigant diffrences. Interfaces are simpler then Excptions and therefore contain fewer configuration properties then Excptions. In genaral any *extra* configuration properties that are in Interfaces will simply be ignored. These will be displayed in the *Interface* tier with **ignored** in the **Required** column.

With the exception of the container properties listed above, each tier can contain any of the properties from any of the tiers above it. However, it cannot contain any properites from any tiers below it. The *Entity* tier can contain any of the configuration properties, but the *Global* tier can only contain the properties defined within it.

Container properties are denoted with **protected** in the **Required** column below. As stated above, these properties *Must* exist at the tier shown and cannot be placed anywhere else in the configuration hierarchy. **eJinn** will throw an exception and let you know if there are any problems in your configuration.

Internal properties are denoted with **private** in the **Required** column below. In general these properties are not accessable outside the `\eJinn\eJinnParser` class and are shown here only for completeness and documentation purposes. They should **not** be included anywere within the configuration.

Comment properties are properties that begin with a `_`. These properties *(and their decendants)* are removed(ignored) from the configuration while it is being proccessed by the parser. This is useful because it allows you to exclude chunks of the configuration without actually deleting them. You can also leave yourself development notes with in the configuration by simply doing something like this `_coment = "eJinn is the most awesomeist thing I ever saw"`.

### Global Tier

[](#global-tier)

PropertyTypeRequiredDescriptionauthorstringnoUsed as the `@author` tag in the Entity's Doc Comment.licensestringnoUsed as the `@license` tag in the Entity's Doc Comment.buildpathmixedno\* See belowdescriptionstringnoPlaced in the opening line of the Entity's Doc Comment.packagestringnoUsed as the `@package` tag in the Entity's Doc Comment.subPackagestringnoUsed as the `@subpackage` tag in the Entity's Doc Comment.supportstringnoUsed as the `@link` tag. This can be a URL or an Email. Support help for your project.versionstringyesUsed as the `@version` tag. Format `major.minor[.revision]`. **eJinn** will recompile the classes if the version is changed.extendsstringnoA base Exception class to extend, default is *PHP's* `\Excption` class. This should be a fully qualified class name.severityintegernoA default severity value, usefull only when entity is a decendant of *PHP's* `\ErrorExcption` class. Also creates class constant `Class::SEVERITY`. The default is `E_USER_ERROR`implementsarraynoArray of fully quallifed interfance names for excptions to impliment. Ignored by interface entities(excptions can impliment multiple interfaces). Interfaces created by **eJinn** are automatically populated where aplicable.reservedarraynoArray of integer codes, or nested arrays `[[min,max]]` for a range of integers. This is a sanity check for *error codes* that should not be created by this configuration.namespacesarrayprotectedArray of namespaces, the `key` should be the namespace which is used by the entities nested in this array.eJinn:HashstringprivateUsed as the `@eJinn:hash` tag. Configuration hash used to check when changes are madeeJinn:BuildVersionstringprivateUsed as the `@eJinn:buildVersion` tag. This allows the **eJinn** project to force a recompile when updates are done to the compiler, seperate from the **eJinn** version number.eJinn:BuildtimefloatprivateUsed as the `@eJinn:buildTime` tag. Time when this config was last parsed *(microtime)*eJinn:Pathnamestringprivateclass Path and filename### Namespace Teir

[](#namespace-teir)

PropertyTypeRequiredDescriptioninterfacesarraynoArray of interface classes that **eJinn** will create ( post-parse ) This is not required but you must have either this property or exceptions, or both.exceptionsarraynoArray of exception classes that **eJinn** will create ( post-parse ) This is not required but you must have either this property or interfaces, or both.namespacestringprivateThe namespace taken from `$global['namespaces'][$namespace]` ie. the key of the namespaces array from the *Global Tier* putting namespace as the key insures no duplacte namespaces are allowed, and it makes to much sense not to do it that way.psrnumberprivatePSR setting at this namespace tier, this is for intarnal storage of the value of buildpath if using the psr array at this level.descriptionstringnoPlaced in the opening line of the Entity's Doc Comment. Overide the global teir### Exception Tier

[](#exception-tier)

PropertyTypeRequiredDescriptionnamestringyesExcption's Class name. Should not include a namespace, it should be the base class name.qulifiedNamestringprivateThe fully qualied class name `namespace\class`descriptionstringnoPlaced in the opening line of the Entity's Doc Comment, overide the global or namespace tiercodeintegeryesExceptions Error code taken from `$namespace['exceptions'][$code]`. The default error code `__construct($message={default},$code=...)`. And a class constant `Class::ERROR_CODE`severityintegernosee Global\[severity\], shown here to offset Interface\[severity\]messagestringnoA default error message `__construct($message={default},$code=...)`### Interface Tier

[](#interface-tier)

PropertyTypeRequiredDescriptionnamestringyesInterface's Class name. Should not include a namespace, it should be the base class name.qulifiedNamestringprivateThe fully qualied class name `namespace\class`descriptionstringnoPlaced in the opening line of the Entity's Doc Comment, overide the global or namespace tiercodeintegerignoredNot Aplicable to this entity typeseverityintegerignoredNot Aplicable to this entity typemessagestringignoredNot Aplicable to this entity typeextendsstringno(see above) will overide above settingsimplementsarrayno(see above) will overide above settings- **buildpath** some special consideration for the *buildpath* property:
    - The default value is the location of the configuration file currently being proccessed.
    - If programmatically running **eJinn**, then this is the second argument of `eJinn\eJinnParser::parse()`.
    - When overriding, this can be either a path relative to parent tier's buildpath, or an absolute path. Unlike other properties that are simply replaced, buildpaths are appended when relative and replaced when absolute. Relative paths should **not** begin with a `/`. Absolute paths should begin with a `/` on Unix based systems and the drive letter on windows `c:\` *(either `/` or `\` is acceptable on window)*.
    - A few *special* values are avalible for the buildpath, These are desinged specifically for autoloaders and are an array instead of the normal string type associated with this property. You may have noticed this in the example configuration. `["psr" => 0]` and `["psr" => 4]`. When using either, the value of the current buildpath *(at that tier)* will have the namespace appended to it with the following considerations:
        - For `["psr" => 0]`: Any `_` underscores in the classname will be replace with a directory seperator. No special considerations are made for `_` underscores in the namespace.
        - For `["psr" => 4]`: No special considerations are made for the `_` underscore.
    - Filepaths should exist, and should be writable by *PHP* running under the current user.

A short build path example:

```
   $config = [
       "buildpath" => "/home/app",  //root path overide with absolute path
       "namespaces" => [
          "Models\\Users\\Exceptions" => [
                "buildpath" => ["psr" => 4],
                "exceptions" => [
                      100 => "UnknownUser",
                      101 => "InvalidPasword",
                ]
          ],
          "Models\\Products\\Exceptions" => [
                "buildpath" => "Models/Products/Exceptions",
                "exceptions" => [
                      200 => "UnknownProduct",
                ]
          ]
      ]
   ];
```

These 2 paths are roughly equivalent, they will product the following files.

- /home/app/Models/Users/Exceptions/UnknownUser.php *(class \\Models\\Users\\Exceptions\\UnknownUser errorCode 100)*
- /home/app/Models/Users/Exceptions/InvalidPasword.php *(class \\Models\\Users\\Exceptions\\InvalidPasword errorCode 101)*
- /home/app/Models/Products/Exceptions/UnknownProduct.php *(class \\Models\\Users\\Exceptions\\UnknownProduct errorCode 200)*

Full PSR-4 example *(assming the configuration file was located in `/home/app`)* this is equivalent to the above example.

```
$config = [
     "buildpath" => ["psr" => 4],
     "namespaces" => [
        "Models\\Users\\Exceptions" => [
              "exceptions" => [
                    100 => "UnknownUser",
                    101 => "InvalidPasword",
              ]
        ],
        "Models\\Products\\Exceptions" => [
              "exceptions" => [
                    200 => "UnknownProduct",
              ]
        ]
    ]
 ];
```

Short PSR-0 example *(assming the configuration file was located in `/home/app`)*

```
$config = [
     "buildpath" => ["psr" => 0],
     "namespaces" => [
        "Models\\Users" => [
              "exceptions" => [
                    100 => "Exception_UnknownUser",
                    101 => "Exception_InvalidPasword",
              ]
        ]
    ]
 ];
```

This will create the folowing 2 classes.

- /home/app/Models/Users/Exception/UnknownUser.php *(class \\Models\\Users\\Exception\_UnknownUser errorCode 100)*
- /home/app/Models/Users/Exception/InvalidPasword.php *(class \\Models\\Users\\Exception\_InvalidPasword errorCode 101)*

PSR-0 allows for shorter namespaces, but still gives the seperation at the file level. This can make is somewhat easier to code as their is less need for `use` statements. But it's slightly more confusing as to where the file is located at. Personally I prefer PSR-4. For example the namespace is simply `Models\Users` so when used within the `Models\Users\User` class, you would not need to include a `use` tag for these exceptions, however the `_` is replaced in the path, putting the exceptions in their own seperate sub-directory. When calling them you would do this `throw Excption_UnknownUser()` instead of just `throw UnknownUser` as the PSR-4 example. However the PSR-4 example also requires this `use Models\Users\Exceptions\{ClassName};` for each exception class. That said, you could also do this in PSR-4 `use Models\Users\Exceptions as Exception;` with aliasing and then throw them like `throw Exeption\UnknownUser()` which is my prefered way.

### Build Options

[](#build-options)

OptionsTypeDescriptionuniqueExceptionsbooleanWhen true will throw an exception if duplicate codes are found in the config, when false duplicates are ignored. This has no effect on codes set as reserved in the config.forceUnlockbooleanIn the event some error occurs that prevents deleteing the `.lock` file you can delete it manually or set this option to `true` to force the parser to run.lockFilestringThe name of the lock file, this should be either the full path and filename, or just the filename. In the case of just a filename the parser buildpath is used `eJinnParser::parse($config, $buildpath, $options)`forceRecompilebooleanThere are serveral ways that a config will be recompiled. You can set this option to `true` to force recompiling to override any caching.cacheFilestringThe name of the cache file, this should be either the full path and filename, or just the filename. In the case of just a filename the parser buildpath is used `eJinnParser::parse($config, $buildpath, $options)`debugbooleanMainly for development. When set to true some debugging information will be outputparseOnlybooleanWhen this is set to `true` only the *parsing* stage is ran. No actual files are created by **eJinn**. This can be useful for doing a dry run.createPathboolean**eJinn** will attempt to build any missing folders in the path of the execption and interfaces. Caution should be taken when using this option. It's suggest to set `['parseOnly'=>true, debug=>true]` options as well the first time it is ran to insure the config will create the proper file locations. Validation on missing folders is bypassed by this option, for obvious reasons. ( if they don't exist, we create them, so no errors for that )### Pre-Reading

[](#pre-reading)

Pre-Reading is defined as the act of opening a configuration file and translating it into the array structure given above. The **eJinn** parser class only understands *PHP* array structure above. By seperating this out into it's own unique step, **eJinn** can use virtual any configuration file type possible.

### Parsing

[](#parsing)

Parsing is defined as the act of taking the *PHP* configuration array and proccessing it. The main job of this step is to flatten out the ineritance tree, and assign all relevent values to either an exception or interface entity. During this step we also check the configuration for various errors.

### Compiling

[](#compiling)

Compiling is defined as the act of creating the actual *PHP* class files for the entities found in the configuration file.

### Locking

[](#locking)

Locking is defined as the act of creating a *eJinn.lock* file, which prevents mulitple processes from running the parser/compiler at the same time. This file will be deleted when the compiling process completes.

### Caching

[](#caching)

Caching is defined as the act of creating a *eJinn.cache* file, this file stores a reference or hash of the config file. This hash is used to tell if any changes are made to the config file, between compilings. If no changes were made the parser will not complile the config. You can delete the *.cache* file to force *eJinn* to recompile all entites. You can also set the runtime option of *forcereCompile* to `true` to cause the parser to recompile the config.

### Exception Considerations

[](#exception-considerations)

Without going into to much detail, I will briefly explain why it's benifical to use unique exceptions. The obvious example is this:

```
   //Catch a single exception ( based on class )
   try{
      throw \Excption("some really verbose message");
   }catch(\Exception $e ){
       echo $e->getMessage(); //prints "?"
   }
```

This is probably the worse exception example I could think of. There is very little you can tell by this what exception it's surpressing or what you should do if it's an acceptable error, or a fatal one. This is a slightly improved version:

```
   //Catch a single exception ( based on class )
   try{
      throw \Excption("some really verbose message", 100);
   }catch(\Exception $e ){
       if($e->getCode() == 200 )
         echo $e->getMessage(); //prints "?"
       else
         throw \Excption("rethrow", $e->getCode(), $e);
   }
```

This still dosn't give us a whole lot of options on catching and ignoring the error. A much better way is something like this:

```
   //Catch a single exception ( based on class )
   try{
      throw \Excption("some really verbose message", 100);
   }catch(\eJinn\Exception\ResservedCode $e ){
       //catch only class \eJinn\Exception\ResservedCode
   }catch(\eJinn\Exception\eJinnExceptionInterface $e ){
       //catch any class that implements \eJinn\Exception\eJinnExceptionInterface
   }
```

Now we have very fine grained control over our error handling. We can catch only the errors we want, and we can handle a range of error in diffrent `catch` blocks. The only problem with this type of error handling is the added hassle in setting the exception classes and keeping track of them.

This is exactly the issue **eJinn** was designed to handle. By simplifing the creation and tracking of these exceptions we can create as many exceptions as we need and have a sinular place to keep track of them.

### Other Configuration Examples

[](#other-configuration-examples)

Minimal Config.

```
return [
    "version"       => "1.0.0",
    "namespaces"     => [
        ""  => [
            "exceptions" => [
                0     => "UnknownError",
            ]
        ]
    ]
];
```

The above configureation will create a single exception file, this will be created at the location of the configuration file with no namespace. So if we had our config at `/home/app/Exceptions` then we would get this file:

- /home/app/Exceptions/UnknownError *( class \\UnknownError errorCode 0)*

\###Examples### **eJinn** uses itself to create it's exception files. You can view the config file at `src/eJinnConf.php` and you can see the files it created at `src/evo/ejinn/Exceptions`. You can also run it through the main `index.php` file. I also plan to use this on my other projects!

\##Version change notes## #v2#

- Relative namespaces, now you can extend exceptions in the same namespace easly
- Simplified the debuging ( no longer needed as much )
- Description added to the local and namespace tiers
- Properly Wordwrap description properly at 90 chars
- Updated all the exceptions **eJinn** may throw itself
- Added a bunch of new extensions that extend the PHP built in ones

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance45

Moderate activity, may be stable

Popularity8

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity82

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 93.3% 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 ~516 days

Recently: every ~595 days

Total

6

Last Release

422d ago

Major Versions

1.0.2 → 2.0.02024-09-09

PHP version history (2 changes)1.0.1PHP &gt;=5.6

2.0.0PHP &gt;=8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/73ff70bb5a9ec9b1a9372e4246373418e694cac33b4353befd95487047d856bc?d=identicon)[ArtisticPhoenix](/maintainers/ArtisticPhoenix)

---

Top Contributors

[![ArtisticPhoenix](https://avatars.githubusercontent.com/u/8208075?v=4)](https://github.com/ArtisticPhoenix "ArtisticPhoenix (210 commits)")[![hdurham](https://avatars.githubusercontent.com/u/53230651?v=4)](https://github.com/hdurham "hdurham (15 commits)")

---

Tags

exception-genieexceptionsphpexceptions

### Embed Badge

![Health badge](/badges/evo-ejinn/health.svg)

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

###  Alternatives

[bugsnag/bugsnag-laravel

Official Bugsnag notifier for Laravel applications.

90234.6M36](/packages/bugsnag-bugsnag-laravel)[bugsnag/bugsnag

Official Bugsnag notifier for PHP applications.

56347.0M75](/packages/bugsnag-bugsnag)[bugsnag/bugsnag-psr-logger

Official Bugsnag PHP PSR Logger.

32132.5M2](/packages/bugsnag-bugsnag-psr-logger)[rollbar/rollbar

Monitors errors and exceptions and reports them to Rollbar

33723.7M81](/packages/rollbar-rollbar)[graham-campbell/exceptions

Provides A Powerful Error Response System For Both Development And Production

5911.3M4](/packages/graham-campbell-exceptions)[bugsnag/bugsnag-symfony

Official BugSnag notifier for Symfony applications.

453.0M3](/packages/bugsnag-bugsnag-symfony)

PHPackages © 2026

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