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

Nsa Laravel Package

nyholm/nsa

Testing helper to access and manipulate private/protected properties and methods in PHP. Set/get instance or static properties and invoke hidden methods to simplify tests and improve DX. Install via Composer: nyholm/nsa.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require --dev nyholm/nsa
    

    Ensure it’s added to require-dev in composer.json to avoid production inclusion.

  2. First Use Case: Test a private method in a Laravel Eloquent model:

    use Nyholm\NSA\NSA;
    
    $user = new User();
    NSA::setProperty($user, 'email_verified_at', now());
    $result = NSA::invokeMethod($user, 'validateVerification', ['email']);
    $this->assertTrue($result);
    
  3. Where to Look First:

    • API Reference: Focus on NSA::getProperty(), NSA::setProperty(), NSA::invokeMethod(), and NSA::getConstant().
    • Laravel-Specific: Use for testing private methods in:
      • Eloquent models (e.g., boot() logic, accessors/mutators).
      • Service classes (e.g., PrivateService::processOrder()).
      • Static properties in abstract classes (e.g., Model::incrementing).

Implementation Patterns

Usage Patterns

  1. Testing Private Model Methods:

    // Test a private model method (e.g., password hashing)
    $user = new User(['password' => 'plaintext']);
    NSA::invokeMethod($user, 'hashPassword');
    $this->assertTrue(Hash::check('plaintext', $user->password));
    
  2. Setting Private Properties in Fixtures:

    // Bypass protected property for test data
    $user = User::factory()->create();
    NSA::setProperty($user, 'api_token', 'test-token-123');
    
  3. Invoking Static Methods:

    // Test static logic in a base class
    $config = NSA::invokeMethod('\App\Services\ConfigService', 'getDefault');
    
  4. Reading Constants:

    // Access private constants (e.g., in a trait)
    $maxRetries = NSA::getConstant('\App\Traits\Retryable', 'MAX_RETRIES');
    
  5. Bulk Property Access:

    // Debug all private properties (e.g., during test development)
    $properties = NSA::getProperties($object);
    dd($properties);
    

Workflows

  1. Test-Driven Development (TDD):

    • Write a test for a private method before implementing it, then refactor to public if needed.
    • Example:
      // test/Unit/UserTest.php
      public function test_private_verification_logic()
      {
          $user = new User();
          NSA::setProperty($user, 'verification_token', 'abc123');
          $result = NSA::invokeMethod($user, 'verifyToken', ['abc123']);
          $this->assertTrue($result);
      }
      
  2. Debugging Complex Objects:

    • Use NSA::getProperties() to inspect the state of a model or service during test development.
    • Example:
      $order = Order::find(1);
      dd(NSA::getProperties($order)); // Inspect private properties
      
  3. Integration with Laravel Factories:

    • Set private properties in factory states:
      User::factory()->state([
          'name' => 'Test User',
      ])->create()->afterCreating(function ($user) {
          NSA::setProperty($user, 'is_admin', true);
      });
      
  4. Testing Abstract Classes:

    • Access static properties in abstract classes (e.g., Illuminate\Database\Eloquent\Model):
      $incrementing = NSA::getProperty('\App\Models\User', 'incrementing');
      $this->assertFalse($incrementing);
      

Integration Tips

  1. Avoid in Production:

    • Add a CI check to block accidental inclusion in autoload:
      # .github/workflows/ci.yml
      - name: Block NSA in production
        run: |
          if grep -r "Nyholm\\\\NSA" src/; then
            echo "NSA found in src/ directory!"
            exit 1
          fi
      
  2. Cache Closures for Performance:

    • Reuse closures for repeated method calls in tests:
      $closure = NSA::getClosure($object, 'privateMethod');
      $result1 = $closure($arg1);
      $result2 = $closure($arg2);
      
  3. Document NSA Usage:

    • Add comments to tests to explain why NSA is used:
      // NSA: Testing private method due to circular dependency with ServiceA
      NSA::invokeMethod($service, 'internalProcess', [$data]);
      
  4. Pair with Refactoring:

    • Use NSA as a temporary tool to test private logic, then refactor to expose behavior via public APIs:
      // Step 1: Test private method with NSA
      NSA::invokeMethod($validator, 'validateInternal', [$data]);
      
      // Step 2: Refactor to public API
      $validator->validateBehavior($data);
      
  5. Laravel-Specific:

    • Test private model events or observers:
      $user = new User();
      NSA::invokeMethod($user, 'fireModelEvent', ['creating']);
      

Gotchas and Tips

Pitfalls

  1. Reflection Limitations:

    • Static Property Modification: NSA cannot set static properties (only read them). Workaround: Use ReflectionClass directly or refactor to instance properties.
      // ❌ Won't work
      NSA::setProperty('\App\Models\User', 'staticProperty', 'value');
      
      // ✅ Workaround
      $reflection = new ReflectionClass('\App\Models\User');
      $property = $reflection->getProperty('staticProperty');
      $property->setAccessible(true);
      $property->setValue(null, 'value');
      
  2. PHP 8+ Behavior Changes:

    • No-Op Methods: NSA 1.3.1+ removes calls to no-op methods in PHP 8.1+. If a method exists but does nothing, it may not execute as expected.
    • Named Arguments: NSA does not support named arguments for method invocation. Pass arguments positionally.
  3. Performance Overhead:

    • Reflection is slower than direct method calls. Cache closures for repeated use:
      $closure = NSA::getClosure($object, 'expensiveMethod');
      
  4. Brittle Tests:

    • Tests using NSA may break if:
      • A private method is renamed or removed.
      • The class hierarchy changes (e.g., moving a method to a trait).
    • Mitigation: Prefer testing behavior over implementation. Use NSA only for logic that cannot be exposed via public APIs.
  5. Laravel-Specific Risks:

    • Framework Internals: Testing private methods in Laravel core classes (e.g., Illuminate\Events\Dispatcher) may break across versions.
    • Service Container: NSA cannot access container-bound methods directly. Use app()->make() first:
      $service = app()->make('\App\Services\PrivateService');
      NSA::invokeMethod($service, 'internalMethod');
      
  6. Thread Safety:

    • NSA is not thread-safe. Avoid using it in concurrent test environments (e.g., parallel PHPUnit runs).

Debugging Tips

  1. Verify Property/Method Exists:

    • Use ReflectionClass to debug:
      $reflection = new ReflectionClass($object);
      if (!$reflection->hasMethod('privateMethod')) {
          $this->fail('Method does not exist');
      }
      
  2. Handle Exceptions:

    • NSA throws exceptions for invalid properties/methods. Catch and assert:
      try {
          NSA::invokeMethod($object, 'nonexistentMethod');
          $this->fail('Expected exception');
      } catch (Exception $e) {
          $this->assertStringContainsString('Method not found', $e->getMessage());
      }
      
  3. Inspect Object State:

    • Use NSA::getProperties() to debug:
      $properties = NSA::getProperties($object);
      $this->assertArrayHasKey('privateField', $properties);
      
  4. Static Property Access:

    • Ensure the class is loaded before accessing static properties:
      if (!class_exists('\App\Models\User')) {
          $this->markTestSkipped('Class not loaded');
      }
      $value = NSA::getProperty('\App\Models\User', 'staticProperty');
      

Extension Points

  1. Custom NSA Instance:

    • Extend NSA for project-specific needs:
      class CustomNSA extends NSA
      {
          public static function getProtectedProperty($object, string $property)
          {
              return parent::getProperty($object, $property);
          }
      }
      
  2. Integration with Pest:

    • Create a Pest helper:
      // tests/TestHelper.php
      use Nyholm\NSA\NSA;
      
      function nsa($object)
      
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.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle