Search by

knops / virustotal-client

IKnops

A PHP client for the VirusTotal API v3

Package info

github.com/knopsmedia/virustotal-client

pkg:composer/knops/virustotal-client

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

v1.0 2025-09-22 09:31 UTC

This package is auto-updated.

Last update: 2026-10-25 03:57:03 UTC


README

A PHP library for scanning files using the VirusTotal API v3. This library follows PSR standards and provides a clean, type-safe interface for file scanning and analysis.

Features

  • ✅ PSR-18 HTTP Client - Uses PSR HTTP standards for maximum compatibility
  • ✅ Type Safety - Full PHP 8.1+ type declarations with strict types
  • ✅ Comprehensive API - Upload files, check analysis status, and get detailed results
  • ✅ Error Handling - Proper exception handling with descriptive messages
  • ✅ Async Support - Poll for results or wait for completion
  • ✅ Protected ZIP Support - Handle password-protected ZIP files
  • ✅ Detailed Results - Access engine-specific results and statistics
  • ✅ Factory Pattern - Easy setup with popular HTTP clients

Installation

Install via Composer:

composer require knops/virustotal-client

For the easiest setup with Guzzle HTTP client:

composer require knops/virustotal-client guzzlehttp/guzzle nyholm/psr7

Quick Start

Using the Factory (Recommended)

<?php

use Knops\VirustotalClient\VirusTotalClientFactory;

// Create client with Guzzle (easiest)
$client = VirusTotalClientFactory::createWithGuzzle('your-api-key-here');

// Scan a file and wait for results
$result = $client->scanFileAndWait('/path/to/file.pdf');

// Check if file is malicious
if ($result->isMalicious()) {
    echo "⚠️  File is malicious! Detected by {$result->getMaliciousCount()} engines\n";
    
    // Get details about malicious detections
    foreach ($result->getMaliciousResults() as $engine => $detection) {
        echo "- {$engine}: {$detection['result']}\n";
    }
} else {
    echo "✅ File is clean!\n";
}

Manual Setup with Custom HTTP Client

<?php

use Knops\VirustotalClient\VirusTotalClient;
use GuzzleHttp\Client;
use Nyholm\Psr7\Factory\Psr17Factory;

$httpClient = new Client();
$psr17Factory = new Psr17Factory();

$client = new VirusTotalClient(
    'your-api-key-here',
    $httpClient,
    $psr17Factory, // Request factory
    $psr17Factory  // Stream factory
);

Usage Examples

Basic File Scanning

// Upload file for scanning
$scanResponse = $client->scanFile('/path/to/suspicious-file.exe');
$analysisId = $scanResponse->getAnalysisId();

// Check analysis status
$analysis = $client->getAnalysis($analysisId);

if ($analysis->isCompleted()) {
    echo "Scan completed!\n";
    echo "Status: " . ($analysis->isClean() ? 'Clean' : 'Threat detected') . "\n";
} else {
    echo "Scan still in progress...\n";
}

Scanning Password-Protected ZIP Files

$result = $client->scanFile('/path/to/protected.zip', 'password123');

Getting Detailed Results

$analysis = $client->getAnalysis($analysisId);

// Get summary information
$summary = $analysis->getSummary();
print_r($summary);

// Get statistics
$stats = $analysis->getStats();
echo "Malicious: {$stats['malicious']}\n";
echo "Suspicious: {$stats['suspicious']}\n";
echo "Clean: {$stats['harmless']}\n";
echo "Undetected: {$stats['undetected']}\n";

// Get file information
echo "File: {$analysis->getFileName()}\n";
echo "Size: {$analysis->getFileSize()} bytes\n";
echo "Type: {$analysis->getFileType()}\n";
echo "SHA256: {$analysis->getFileHash()}\n";

Advanced: Custom Polling

$scanResponse = $client->scanFile('/path/to/file.pdf');
$analysisId = $scanResponse->getAnalysisId();

// Poll every 5 seconds for up to 2 minutes
$maxWait = 120; // seconds
$pollInterval = 5; // seconds

$startTime = time();
while (time() - $startTime < $maxWait) {
    $analysis = $client->getAnalysis($analysisId);
    
    if ($analysis->isCompleted()) {
        echo "Analysis completed!\n";
        break;
    }
    
    echo "Still scanning... ({$analysis->getStatus()})\n";
    sleep($pollInterval);
}

API Reference

VirusTotalClient

Methods

  • scanFile(string $filePath, ?string $password = null): ScanResponse

    • Upload a file for scanning
    • Returns scan response with analysis ID
  • getAnalysis(string $analysisId): AnalysisResponse

    • Get analysis results by ID
    • Returns detailed analysis response
  • scanFileAndWait(string $filePath, ?string $password = null, int $maxWaitTime = 300, int $pollInterval = 10): AnalysisResponse

    • Scan file and wait for completion
    • Automatically polls for results

ScanResponse

Methods

  • getAnalysisId(): string - Get the analysis ID for checking results
  • getType(): string - Get the response type
  • getRawData(): array - Get raw API response data

AnalysisResponse

Status Methods

  • isCompleted(): bool - Check if analysis is finished
  • getStatus(): string - Get current analysis status
  • isMalicious(): bool - Check if file has malicious detections
  • isSuspicious(): bool - Check if file has suspicious detections
  • isClean(): bool - Check if file is clean

Statistics Methods

  • getStats(): array - Get detection statistics
  • getMaliciousCount(): int - Number of malicious detections
  • getSuspiciousCount(): int - Number of suspicious detections
  • getTotalEngines(): int - Total number of scanning engines

Results Methods

  • getEngineResults(): array - Get all engine results
  • getMaliciousResults(): array - Get only malicious detections
  • getSuspiciousResults(): array - Get only suspicious detections

File Information Methods

  • getFileHash(): ?string - Get SHA256 hash
  • getFileName(): ?string - Get original filename
  • getFileSize(): ?int - Get file size in bytes
  • getFileType(): ?string - Get file type description
  • getScanDate(): ?int - Get scan timestamp

Utility Methods

  • getSummary(): array - Get comprehensive summary
  • getRawData(): array - Get raw API response data

Error Handling

The library throws VirusTotalException for various error conditions:

use Knops\VirustotalClient\Exception\VirusTotalException;

try {
    $result = $client->scanFile('/path/to/file.pdf');
} catch (VirusTotalException $e) {
    echo "Error: " . $e->getMessage() . "\n";
    
    // Handle specific error cases
    if (str_contains($e->getMessage(), 'File not found')) {
        echo "The specified file does not exist.\n";
    } elseif (str_contains($e->getMessage(), 'API request failed')) {
        echo "There was an issue with the VirusTotal API.\n";
    }
}

Requirements

  • PHP 8.1 or higher
  • PSR-18 HTTP Client implementation
  • PSR-17 HTTP Factory implementation
  • VirusTotal API key

Getting a VirusTotal API Key

  1. Sign up at VirusTotal
  2. Go to your profile settings
  3. Copy your API key from the API Key section

Rate Limits

VirusTotal has rate limits based on your account type:

  • Public API: 4 requests per minute
  • Premium API: Higher limits based on subscription

The library does not automatically handle rate limiting, so you may need to implement delays between requests for high-volume usage.

Testing

Run the test suite:

composer install
vendor/bin/phpunit

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass
  6. Submit a pull request

License

This library is licensed under the MIT License. See the LICENSE file for details.

Security

If you discover any security-related issues, please email ignace@knopsmedia.be instead of using the issue tracker.

Changelog

1.0.0

  • Initial release
  • File scanning functionality
  • Analysis result retrieval
  • PSR HTTP client support
  • Comprehensive test coverage