PHPackages                             onix-systems-php/hyperf-scorm - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. onix-systems-php/hyperf-scorm

ActiveExtension

onix-systems-php/hyperf-scorm
=============================

Onix Hyperf SCORM extension

v1.1.2-beta(5mo ago)00MITPHPPHP &gt;=8.1

Since Nov 28Pushed 5mo agoCompare

[ Source](https://github.com/onix-systems-php/hyperf-scorm)[ Packagist](https://packagist.org/packages/onix-systems-php/hyperf-scorm)[ RSS](/packages/onix-systems-php-hyperf-scorm/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (20)Versions (3)Used By (0)

Hyperf SCORM Package
====================

[](#hyperf-scorm-package)

[![PHP Version](https://camo.githubusercontent.com/7663c9d53dc13cedaf0660a8745a7e77d2dd711257f36aa86ebce12a0600ef42/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344382e312d626c75652e737667)](https://php.net)[![Hyperf Version](https://camo.githubusercontent.com/59229241056eb5aedefd8c5a47a8ffeeb4447215d7c0d61884eff4813c5e9610/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6879706572662d253545332e312d627269676874677265656e2e737667)](https://hyperf.io)[![License](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)

Enterprise-grade SCORM package for Hyperf with Gateway architecture, async processing, and real-time WebSocket progress tracking.

Features
--------

[](#features)

- ✅ **SCORM 1.2 &amp; 2004** - Full compliance, auto-detect version
- ✅ **Gateway Architecture** - Clean separation of concerns, stable public API
- ✅ **Async Processing** - Queue-based handling with configurable threshold
- ✅ **Real-time Progress** - WebSocket notifications with fallback
- ✅ **Customizable Auth** - Published controllers for flexible authentication integration
- ✅ **Flexible Storage** - S3, local storage, or custom drivers

Requirements
------------

[](#requirements)

- PHP 8.2+ (8.1 supported), Hyperf 3.1.42+
- Redis, PostgreSQL 13+ or MySQL 8.0+
- Swoole 5.0+

Installation
------------

[](#installation)

### Step 1: Install Package

[](#step-1-install-package)

```
composer require onix-systems-php/hyperf-scorm
```

### Step 2: Publish Configuration

[](#step-2-publish-configuration)

```
php bin/hyperf.php vendor:publish onix-systems-php/hyperf-scorm --id=scorm_config
```

### Step 3: Publish Migrations

[](#step-3-publish-migrations)

```
php bin/hyperf.php vendor:publish onix-systems-php/hyperf-scorm --id=scorm_migrations
```

### Step 4: Publish Controller (REQUIRED)

[](#step-4-publish-controller-required)

```
php bin/hyperf.php vendor:publish onix-systems-php/hyperf-scorm --id=scorm_controller
```

This publishes `ScormController` to `app/Scorm/Controller/ScormController.php` where you'll integrate your authentication system and configure authorization (ACL).

⚠️ **Important:** You MUST customize the published controller to integrate your authentication before using in production.

### Step 5: Publish Example Files (Optional)

[](#step-5-publish-example-files-optional)

```
php bin/hyperf.php vendor:publish onix-systems-php/hyperf-scorm --id=scorm_example
```

### Step 6: Run Migrations

[](#step-6-run-migrations)

```
php bin/hyperf.php migrate
```

### Step 7: Register Routes

[](#step-7-register-routes)

Add to `config/routes.php`:

```
// Package-provided API routes (SCORM player, job status, WebSocket)
require_once './vendor/onix-systems-php/hyperf-scorm/publish/routes.php';

// Your published controller routes are auto-registered via Controller annotation
```

Quick Start
-----------

[](#quick-start)

### 1. Configure Environment

[](#1-configure-environment)

```
# Essential settings
SCORM_STORAGE_DRIVER=local           # scormS3|local
SCORM_MAX_FILE_SIZE=100              # MB
SCORM_WS_NAME=socket-io
SCORM_DEBUG=false

# Processing
SCORM_AUTO_COMMIT_INTERVAL=30        # seconds
SCORM_CACHE_TTL=3600                 # seconds
```

### 2. Integrate Authentication

[](#2-integrate-authentication)

Edit published `app/Scorm/Controller/ScormController.php`:

```
use OnixSystemsPHP\HyperfAuth\AuthManager;
use OnixSystemsPHP\HyperfAuth\Annotation\Acl;
use App\User\Constants\UserRoles;

#[Controller(prefix: 'v1/scorm')]
class ScormController extends AbstractController
{
    public function __construct(
        private readonly ScormGatewayInterface $scormGateway,
        private readonly AuthManager $authManager  // Add your auth
    ) {}

    #[PostMapping(path: 'packages/upload')]
    #[Acl(roles: [UserRoles::GROUP_ADMINS])]  // Your ACL rules
    public function upload(RequestUploadScormPackage $request): ResourceScormAsyncJob
    {
        // Extract userId from your auth system
        $userId = $this->authManager->user()->id;

        // Use gateway - business logic abstracted
        $jobDTO = $this->scormGateway->upload(
            ScormUploadDTO::make($request),
            $userId
        );

        return ResourceScormAsyncJob::make($jobDTO);
    }
}
```

### 3. Upload Package

[](#3-upload-package)

```
curl -X POST https://your-api.com/v1/scorm/packages/upload \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "file=@package.zip" \
  -F "title=Introduction Course" \
  -F "description=Basic training module"
```

**Response:**

```
{
    "data": {
        "job_id": "31ee577a-3409-4228-950e-82eea8f9c172",
        "status": "queued",
        "progress": 0,
        "stage": "queued",
        "estimated_time": 433,
        "is_async": true
    },
    "status": 200
}
```

### 4. Track Progress (WebSocket)

[](#4-track-progress-websocket)

```
const jobId = response.data.job_id;
const ws = new WebSocket(
  `ws://your-domain:9502/scorm-progress?job_id=${jobId}`
);

ws.onmessage = (event) => {
  const { stage, progress, status, package_id } = JSON.parse(event.data).data;
  console.log(`${stage}: ${progress}%`);

  if (status === 'completed') {
    console.log(`Package ready: ${package_id}`);
    ws.close();
  }
};
```

### 5. Launch SCORM Player

[](#5-launch-scorm-player)

```
curl -X GET https://your-api.com/v1/scorm/player/123/launch \
  -H "Authorization: Bearer YOUR_TOKEN"

# Returns HTML player page with embedded SCORM content
```

Gateway API
-----------

[](#gateway-api)

The package uses Gateway pattern to provide a clean, stable public API. The `ScormGatewayInterface` extends three specialized interfaces for focused responsibilities:

```
interface ScormGatewayInterface extends
    ScormPlayerGatewayInterface,      // Launch player
    ScormProgressGatewayInterface,     // Job tracking
    ScormPackageGatewayInterface       // Package management
{
}
```

### ScormPackageGatewayInterface

[](#scormpackagegatewayinterface)

**Upload SCORM package (async):**

```
upload(ScormUploadDTO $dto, int $userId): ScormAsyncJobDTO
```

**List packages with pagination:**

```
index(array $filters, PaginationRequestDTO $pagination): PaginationResultDTO
```

**Delete package and all related data:**

```
destroy(int $packageId): ScormPackage
```

### ScormPlayerGatewayInterface

[](#scormplayergatewayinterface)

**Launch SCORM player:**

```
launch(int $packageId): ScormPlayerDTO
```

Returns `ScormPlayerDTO`:

- `package` (ScormPackageDTO): Package information
- `playerHtml` (string): Complete HTML player page

### ScormProgressGatewayInterface

[](#scormprogressgatewayinterface)

**Get job status:**

```
statusJob(string $jobId): ResourceScormJobStatus
```

**Cancel queued job:**

```
cancelJob(string $jobId): array
```

### Usage Example

[](#usage-example)

```
use OnixSystemsPHP\HyperfScorm\Contract\Gateway\ScormGatewayInterface;

class YourController
{
    public function __construct(
        private readonly ScormGatewayInterface $scormGateway
    ) {}

    public function someAction()
    {
        // Upload package
        $jobDTO = $this->scormGateway->upload($dto, $userId);

        // List packages
        $result = $this->scormGateway->index($filters, $pagination);

        // Launch player
        $playerDTO = $this->scormGateway->launch($packageId);

        // Track job
        $status = $this->scormGateway->statusJob($jobId);
    }
}
```

Configuration
-------------

[](#configuration)

All configuration is in `config/autoload/scorm.php` (published in installation step 2).

### Environment Variables

[](#environment-variables)

VariableDefaultUnitDescription**Storage Configuration**`SCORM_STORAGE_DRIVER``local`-Storage driver: `scormS3` or `local``SCORM_MAX_FILE_SIZE``100`MBMaximum SCORM package upload size**S3 Storage (when driver=s3)**`SCORM_S3_KEY`--AWS access key ID (required for S3)`SCORM_S3_SECRET`--AWS secret access key (required for S3)`SCORM_S3_REGION``us-east-1`-AWS region for S3 bucket`SCORM_S3_BUCKET``scorm-content`-S3 bucket name for SCORM content`SCORM_S3_ENDPOINT`--Custom S3 endpoint (for S3-compatible services)`SCORM_S3_PATH_STYLE``false`-Use path-style endpoint (true for MinIO/custom S3)`SCORM_S3_DOMAIN`--Custom domain for S3 public URLs (CDN)**API &amp; Player Configuration**`SCORM_API_TIMEOUT``30000`msAPI request timeout in milliseconds`SCORM_DEBUG``false`-Enable debug mode (verbose logging)`SCORM_AUTO_COMMIT_INTERVAL``30`secAuto-commit interval for CMI data**WebSocket Configuration**`SCORM_WS_NAME``socket-io`-WebSocket server name identifier**Cache Configuration**`SCORM_CACHE_TTL``3600`secCache time-to-live for SCORM data**Redis TTL Configuration**`SCORM_REDIS_TTL_JOB_STATUS``3600`secJob status cache TTL (1 hour)`SCORM_REDIS_TTL_JOB_RESULT``86400`secJob result cache TTL (24 hours)`SCORM_REDIS_TTL_WEBSOCKET``86400`secWebSocket connection data TTL (24 hours)WebSocket Server Setup
----------------------

[](#websocket-server-setup)

WebSocket provides real-time progress updates. Configure in `config/autoload/server.php`:

```
return [
    'servers' => [
        [
            'name' => 'socket',
            'type' => Server::SERVER_WEBSOCKET,
            'host' => '0.0.0.0',
            'port' => 9502,  // NOTICE: This port must be open on the server
            'callbacks' => [
                Event::ON_HAND_SHAKE => [Hyperf\WebSocketServer\Server::class, 'onHandShake'],
                Event::ON_MESSAGE => [Hyperf\WebSocketServer\Server::class, 'onMessage'],
                Event::ON_CLOSE => [Hyperf\WebSocketServer\Server::class, 'onClose'],
            ],
            'settings' => [
                Constant::OPTION_HEARTBEAT_CHECK_INTERVAL => 60,
                Constant::OPTION_HEARTBEAT_IDLE_TIME => 600,
            ],
        ],
    ],
];
```

### Connection Flow

[](#connection-flow)

1. Connect: `ws://host:9502/scorm-progress?job_id={uuid}`
2. Receive progress: `{"type":"progress","data":{"stage":"extracting","progress":45}}`
3. Heartbeat: Server pings every 30s, client responds with pong
4. Complete: `{"type":"progress","data":{"status":"completed","package_id":123}}`

### Message Format

[](#message-format)

```
{
  "type": "progress",
  "data": {
    "job_id": "uuid",
    "status": "processing",
    "stage": "extracting",
    "progress": 45,
    "package_id": null,
    "error": null
  }
}
```

API Endpoints
-------------

[](#api-endpoints)

### Published Controller Endpoints

[](#published-controller-endpoints)

Managed by your published `app/Scorm/Controller/ScormController.php`:

MethodEndpointDescriptionAuthPOST`/v1/scorm/packages/upload`Upload SCORM packageRequiredGET`/v1/scorm/packages`List packages (paginated)RequiredGET`/v1/scorm/player/{id}/launch`Launch SCORM playerRequiredDELETE`/v1/scorm/packages/{id}`Delete packageRequiredGET`/v1/scorm/jobs/{jobId}/status`Get job statusRequiredPOST`/v1/scorm/jobs/{jobId}/cancel`Cancel jobRequired**Note:** You control auth/ACL in your published controller.

### Package-Provided Endpoints

[](#package-provided-endpoints)

Internal API routes (auto-registered via `publish/routes.php`):

MethodEndpointDescriptionAuthGET`/v1/api/scorm/{packageId}/users/{userId}/initialize`Initialize SCORM sessionNone (internal)POST`/v1/api/scorm/{packageId}/commit/{sessionToken}`Save SCORM dataNone (token-based)WS`ws://host:9502/scorm-progress?job_id={id}`Real-time progressNone (jobId-based)**Note:** Internal endpoints use session tokens/job IDs for security, not user auth.

SCORM Support
-------------

[](#scorm-support)

FeatureSCORM 1.2SCORM 2004Manifest Parsing✅✅Auto Version Detection✅✅Multiple SCOs✅✅CMI Data Model✅✅Sequencing Rules❌✅Interactions✅✅**Supported schemaversion:**

- **1.2**: `1.2`, `CAM 1.2`
- **2004**: `CAM 1.3`, `2004`, `2004 3rd Edition`, `2004 4th Edition`

Troubleshooting
---------------

[](#troubleshooting)

### Updating from hyperf-core 1.2 to 1.3

[](#updating-from-hyperf-core-12-to-13)

If you have an older version of the `hyperf-core` package (1.2) installed, you need to update it along with the `plain-to-class` library:

```
composer update onix-systems-php/hyperf-core yzen.dev/plain-to-class
```

This ensures compatibility between the core package and the SCORM package dependencies.

### UploadedFile Type Conversion Issue (plain-to-class v3.0+)

[](#uploadedfile-type-conversion-issue-plain-to-class-v30)

If you're using `yzen.dev/plain-to-class` v3.0+ and encounter a class validation error with `UploadedFile` type (e.g., `Hyperf\HttpMessage\Upload\UploadedFile`), you need to add a custom setter in your DTO.

**Solution:**

Add a custom setter for the `UploadedFile` field in your DTO class:

```
use Hyperf\HttpMessage\Upload\UploadedFile;

class YourDTO
{
    public UploadedFile $file;

    public function setFileAttribute(UploadedFile $value): void
    {
        $this->file = $value;
    }
}
```

For more details, see the [plain-to-class custom setter documentation](https://github.com/yzen-dev/plain-to-class/tree/feature/v3.0.0-dev?tab=readme-ov-file#custom-setter).

License
-------

[](#license)

This package is open-sourced software licensed under the [MIT license](LICENSE).

###  Health Score

28

—

LowBetter than 54% of packages

Maintenance70

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity31

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

171d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/8e1c2e991197c9603eb160eb27e1be423eb683ee2eed293288993936444c20bb?d=identicon)[onix-systems-php](/maintainers/onix-systems-php)

---

Top Contributors

[![tahyhax](https://avatars.githubusercontent.com/u/7867874?v=4)](https://github.com/tahyhax "tahyhax (107 commits)")

---

Tags

phphyperfscorm

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/onix-systems-php-hyperf-scorm/health.svg)

```
[![Health](https://phpackages.com/badges/onix-systems-php-hyperf-scorm/health.svg)](https://phpackages.com/packages/onix-systems-php-hyperf-scorm)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
