Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pops Laravel Package

eloquent/pops

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require eloquent/pops
    

    (Note: Despite being archived, the package remains functional for PHP 8.1+.)

  2. First Use Case: Wrap an object to modify its behavior transparently. Example:

    use Eloquent\Pops\ProxyObject;
    
    class UppercaseProxy extends ProxyObject {
        public function popsCall($method, array &$arguments) {
            return strtoupper(parent::popsCall($method, $arguments));
        }
    }
    
    $original = new stdClass();
    $original->message = "hello";
    
    $proxy = new UppercaseProxy($original);
    echo $proxy->message; // Outputs: "HELLO"
    
  3. Key Classes:

    • ProxyObject: For object wrapping.
    • ProxyArray: For array wrapping.
    • ProxyPrimitive: For primitive values (e.g., strings, numbers).
    • SafeProxy: Mark objects/values to exclude from recursive proxying.

Where to Look First

  • Documentation: Focus on the README for core concepts (e.g., popsCall(), __get(), recursive proxies).
  • Interfaces: Review ProxyInterface, SafeInterface, and type-specific interfaces (e.g., ProxyObjectInterface) for method signatures.
  • Examples: Study the OutputEscaper example for recursive proxying patterns.

Implementation Patterns

Core Workflows

  1. Transparent Method Interception: Override popsCall() to modify method return values or arguments:

    class LoggingProxy extends ProxyObject {
        public function popsCall($method, array &$arguments) {
            $result = parent::popsCall($method, $arguments);
            logger()->info("Method {$method} called, returned: " . json_encode($result));
            return $result;
        }
    }
    
  2. Property Modification: Override __get()/__set() to transform properties:

    class SanitizedProxy extends ProxyObject {
        public function __get($property) {
            return htmlspecialchars(parent::__get($property));
        }
    }
    
  3. Recursive Proxying: Use Proxy::proxy() with nested structures (arrays/objects):

    $data = ['user' => ['name' => 'Alice', 'email' => 'alice@example.com']];
    $proxy = Proxy::proxy($data, true); // Recursively wrap all values
    echo $proxy['user']['name']; // Escaped/sanitized if proxy handles it
    
  4. Safe Values: Exclude specific values from proxying:

    $safeValue = SafeProxy::proxy("Do not modify me!");
    $proxy = Proxy::proxy(['safe' => $safeValue, 'normal' => 'modify']);
    

Integration Tips

  • Laravel Service Providers: Register a global proxy wrapper for requests/responses:

    public function boot() {
        $this->app->resolving('request', function ($request) {
            return Proxy::proxy($request, true);
        });
    }
    
  • Middleware: Use proxies to sanitize input/output:

    public function handle($request, Closure $next) {
        $sanitizedRequest = Proxy::proxy($request->all(), true);
        return $next($sanitizedRequest);
    }
    
  • Eloquent Models: Wrap model attributes for validation/logging:

    class User extends Model {
        public function getAttributes() {
            return Proxy::proxy(parent::getAttributes(), true);
        }
    }
    
  • API Responses: Apply consistent formatting to JSON responses:

    return Proxy::proxy($data, true)->toJson();
    

Common Patterns

Pattern Use Case Example Class
Method Wrapper Modify method return values LoggingProxy
Property Wrapper Sanitize/transform properties SanitizedProxy
Recursive Wrapper Deeply nest proxies (e.g., arrays) OutputEscaperProxy
Safe Guard Exclude values from proxying SafeProxy
Type-Specific Handle primitives/arrays/objects ProxyPrimitive, ProxyArray

Gotchas and Tips

Pitfalls

  1. Reference Arguments:

    • Issue: Methods with &$argument cannot be called directly via $proxy->method($arg).
    • Fix: Use popsCall() with pre-bound references:
      $var = null;
      $proxy->popsCall('method', [&$var]);
      
  2. Recursive Overhead:

    • Issue: Deep recursion can impact performance.
    • Fix: Use SafeProxy for large/unmodifiable structures or limit recursion depth.
  3. Magic Methods Conflicts:

    • Issue: Overriding __call()/__get() may clash with PHP’s magic methods.
    • Fix: Prefer popsCall() for methods and __get()/__set() for properties.
  4. PHP 8.1+ Deprecations:

    • Issue: Some methods (e.g., popsArray()) are deprecated in favor of popsValue().
    • Fix: Update to use ProxyInterface::popsValue():
      $value = $proxy->popsValue(); // Replaces type-specific methods
      
  5. Circular References:

    • Issue: Proxies may cause infinite loops with circular references (e.g., A->B->A).
    • Fix: Implement a visited tracker in recursive proxies or use SafeProxy for shared objects.

Debugging Tips

  • Verify Proxy Chain: Use get_class($proxy) to confirm the proxy type. For nested proxies, recursively check each level.

  • Log Proxy Calls: Add debug output in popsCall() to trace method invocations:

    public function popsCall($method, array &$arguments) {
        logger()->debug("Proxying {$method} on " . get_class($this->popsValue()));
        return parent::popsCall($method, $arguments);
    }
    
  • Check for SafeProxies: If a value isn’t being proxied, ensure it’s not wrapped in SafeProxy:

    if ($value instanceof SafeInterface) {
        logger()->warning("Skipping SafeProxy for: " . gettype($value->popsValue()));
    }
    

Extension Points

  1. Custom Proxy Classes: Extend base classes (ProxyObject, ProxyArray) to add domain-specific logic:

    class CachedProxy extends ProxyObject {
        private $cache = [];
    
        public function popsCall($method, array &$arguments) {
            $key = md5($method . serialize($arguments));
            return $this->cache[$key] ?? ($this->cache[$key] = parent::popsCall($method, $arguments));
        }
    }
    
  2. Dynamic Proxying: Use Proxy::proxy() with closures to conditionally wrap values:

    $proxy = Proxy::proxy($value, function ($value) {
        return is_string($value) && strpos($value, '<script>') !== false;
    });
    
  3. Interface Enforcement: Ensure proxies implement ProxyInterface to guarantee consistency:

    class MyProxy extends ProxyObject implements ProxyInterface {
        // ...
    }
    
  4. Integration with Laravel:

    • Service Container: Bind proxies to interfaces for dependency injection.
    • Facades: Create facades for common proxy operations (e.g., Proxy::sanitize()).

Configuration Quirks

  • Recursion Depth: The package doesn’t limit recursion depth by default. For large structures, set a custom limit:

    Proxy::setMaxRecursionDepth(10); // Default is often higher
    
  • Primitive Handling: Primitives (e.g., int, string) are wrapped in ProxyPrimitive. Override __toString() for custom behavior:

    class FormattedProxyPrimitive extends ProxyPrimitive {
        public function __toString() {
            return date('Y-m-d', $this->popsValue());
        }
    }
    
  • Type Juggler: Proxies preserve the original type. For example, a proxied int remains an int (but wrapped). Cast explicitly if needed:

    $proxy = Proxy::proxy(42);
    $int = (int) $proxy; // Explicit cast
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor