Skip to content

Instantly share code, notes, and snippets.

@spiechu
Last active January 3, 2016 01:19
Show Gist options
  • Save spiechu/8388636 to your computer and use it in GitHub Desktop.
Save spiechu/8388636 to your computer and use it in GitHub Desktop.
<?php
interface CommonErrorCodes {
const ERR_STATUS_OK = 0;
const ERR_STATUS_NOT_FOUND = 1;
const ERR_STATUS_INVALID = 2;
const CHECK_ERR_PREFIX = 'ERR_';
}
trait CheckClassConst {
/**
* Cache reflection class results.
*
* @var array [className => [constName => constValue], ...]
*/
static private $classConstants = [];
/**
* Force class to implement valid constant names.
*
* Return '' to take all class constants or narrow names e.g. 'ERR'.
*
* @return string
*/
abstract protected function getConstPrefix();
/**
* Check $value against valid constant values.
*
* @param mixed $value
* @return boolean
*/
public function isConstValueOK($value) {
// Current class is forced to implement this abstract method.
$constantPrefix = $this->getConstPrefix();
$prefixRegex = '/^' . $constantPrefix . '/';
foreach ($this->getClassConstants() as $constName => $constVal) {
// When values match, check if constant name also match.
if ($value === $constVal && (empty($constantPrefix) || preg_match($prefixRegex, $constName))) {
return true;
}
}
return false;
}
/**
* Returns class constants.
*
* @return array [constName => constValue, ...]
*/
protected function getClassConstants() {
$currentClass = get_class($this);
// Try to use static cache.
if (!array_key_exists($currentClass, self::$classConstants)) {
$reflection = new \ReflectionObject($this);
// This is where the magic works.
self::$classConstants[$currentClass] = $reflection->getConstants();
}
return self::$classConstants[$currentClass];
}
}
class BaseClass implements CommonErrorCodes {
const GARBAGE_STATUS = 8;
use CheckClassConst {
// Narrow isConstValueOK() visibility to protected.
// Don't let trait method leak to public API.
isConstValueOK as protected;
}
/**
* @var integer
*/
protected $status;
/**
* Sets object status code.
*
* @param integer $status one of CommonErrorCodes::ERR_STATUS_*
* @throws \Exception when bad status code provided
*/
public function setStatus($status) {
// This is where the magic works.
if (!$this->isConstValueOK($status)) {
throw new \Exception('Wrong status provided');
}
$this->status = $status;
}
/**
* Implements trait abstract method.
*
* @return string
*/
protected function getConstPrefix() {
return CommonErrorCodes::CHECK_ERR_PREFIX;
}
}
class Subclass extends BaseClass {
const GARBAGE_STATUS_EXTENDED = 9;
/**
* Change valid statuses to GARBAGEs.
*
* @return string
*/
protected function getConstPrefix() {
return 'GARBAGE_';
}
}
$base = new BaseClass();
$base->setStatus(0); // true
$base->setStatus(8); // Exception thrown because of it's GARBAGE_STATUS value
$sub = new Subclass();
$sub->setStatus(8); // true because of getConstPrefix()'s Subclass returns 'GARBAGE_'
$sub->setStatus(9); // true
$sub->setStatus(0); // Exception
@potfur
Copy link

potfur commented May 7, 2014

IMHO this should be done with SplEnum (or similar) not traits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment