delormejonathan/accessible
Accessible is a PHP library that uses docblock annotations to automate class behavior: generates getters/setters, validates setter arguments with Symfony Assert, initializes properties in constructors, and manages collections and associations with add/remove helpers.
Installation
composer require delormejonathan/accessible
Add the Accessible trait to your model or class:
use Delorme\Accessible\Accessible;
class User extends Model
{
use Accessible;
}
Define Annotations Use PHPDoc annotations to control access to properties/methods:
/**
* @access public
*/
public $name;
/**
* @access protected
*/
public $email;
/**
* @access private
*/
public $apiToken;
First Use Case Access properties while respecting annotations:
$user = new User();
$user->name = 'John'; // Works (public)
$user->email = '[email protected]'; // Throws exception (protected)
src/Accessible.php for supported annotations (@access, @read-only, @write-only).src/Exceptions/ for error handling (e.g., AccessDeniedException).php artisan test (if included) or manually test edge cases.Model-Level Access Control
Apply Accessible to Eloquent models to enforce business logic rules:
class Order extends Model
{
use Accessible;
/**
* @access public
* @read-only
*/
public $total;
/**
* @access protected
*/
public $customerId;
}
total or customerId in controllers.Dynamic Property Access Use annotations to gate dynamic properties (e.g., API responses):
class UserResource extends JsonResource
{
public function toArray($request)
{
$user = new User();
return [
'name' => $user->name, // Respects @access public
'email' => $user->email, // Fails silently or throws
];
}
}
Integration with Laravel Policies Combine with Laravel’s built-in policies for layered security:
// Policy
public function update(User $user, Order $order)
{
if ($order->customerId !== $user->id) {
throw new AccessDeniedException("Cannot modify non-owned orders.");
}
return true;
}
Accessible::setAccessible() to temporarily override rules:
Accessible::setAccessible($user, ['email'], 'public'); // Allow email writes
accessible() helper:
$filtered = collect($users)->map->accessible(['name', 'email']);
$user = new User();
$user->setAccessibleProperty('email', 'public'); // Override for tests
Annotation Parsing Quirks
@access is on the property line.@Access (wrong case) will fail silently. Use @access.__get()/__set() bypass annotations. Avoid overriding these unless intentional.Performance Overhead
$parser = new \Delorme\Accessible\Parser();
$parser->parse($user); // Cache result if needed
Laravel-Specific Issues
session()). Re-parse after deserialization.fill() ignores annotations. Use Accessible::fill() instead:
$user->accessibleFill(['name' => 'John']); // Respects @access
Edge Cases
User->orders->items) may cause infinite loops. Use maxDepth in parser:
$parser->setMaxDepth(2);
ACCESSIBLE_DEBUG=true in .env to log denied accesses.dd($user->getAccessibleProperties());
Accessible trait to customize behavior:
trait CustomAccessible extends Accessible
{
protected function onAccessDenied()
{
Log::warning("Access denied to {$this->property}");
return null; // Silent fail
}
}
Custom Annotations
Extend the parser to support new annotations (e.g., @validate):
// In a service provider
$parser->addAnnotationHandler('validate', function ($value) {
return validator()->make(['field' => $value], ['field' => 'required|email']);
});
Dynamic Access Control
Use the Accessible::canAccess() method to implement runtime checks:
if (Accessible::canAccess($user, 'email', 'write')) {
$user->email = '[email protected]';
}
Integration with Laravel Events Trigger events on access attempts:
Accessible::onAccessAttempt(function ($object, $property, $accessType) {
event(new AccessAttempted($object, $property, $accessType));
});
How can I help you explore Laravel packages today?