Sebelum memulai praktik, pastikan Anda telah memahami konsep dasar pada materi kelas AI Agent & PHP di infokoding.

BAB 13

Membuat Tool Membaca File AI Agent PHP

Implementasi tool pembaca file untuk AI Agent PHP: ekstraksi konten dari PDF, TXT, DOCX, CSV, dan JSON dengan keamanan path traversal protection dan batasan ukuran file.

Terakhir diperbarui:

TL;DR — Ringkasan Tool Membaca File AI Agent PHP

  • Proteksi Path Traversal: Gabungkan fungsi basename() dan verifikasi realpath() terhadap direktori yang diizinkan untuk mencegah akses file sistem di luar folder upload.
  • Pembatasan Ukuran File: Batasi file maksimal ~10 MB untuk mencegah kehabisan memori server (*Memory Exhaustion*).
  • Ekstraksi Multi-Format: Dukung format teks populer (PDF, DOCX, CSV, JSON, TXT) via library parser terisolasi.
  • Token Truncation Buffer: Potong output teks pada batas karakter aman (misal: 8.000 karakter) guna mencegah lonjakan token context window LLM.
Prerequisites & Kebutuhan Sistem AI Agent PHP FileTool:
PHP 8.2+ Runtime:Ekstensi mbstring, zip (untuk DOCX), dan fileinfo wajib aktif.
Composer Package:smalot/pdfparser dan phpoffice/phpword terpasang via autoloader.
Direktori Uploads Aman:Folder khusus (misal: ./uploads) dengan hak akses baca (read permission).
Konfigurasi Memory:memory_limit PHP minimal 128 MB untuk parsing dokumen biner.

Instalasi Library Parsing File di PHP

Untuk membaca dokumen biner seperti PDF dan DOCX secara native tanpa ketergantungan utility sistem operasi luar, pasang library parser standar industri melalui Composer:

composer require smalot/pdfparser phpoffice/phpword
Konfigurasi composer.json Lengkap (File Parsing Stack)

Catatan Teknis Ekstraksi PDF (Vector Layer vs OCR):

Library smalot/pdfparser bekerja murni mengekstrak teks berbasis layer teks/vektor asli (dokumen PDF digital yang dihasilkan dari Microsoft Word, Google Docs, atau ekspor sistem). Jika file PDF berupa dokumen hasil scan foto/gambar bitmap, Anda memerlukan engine OCR eksternal seperti Tesseract OCR atau integrasi Multimodal Vision API (seperti GPT-4o Vision) untuk membaca teks di dalam gambar tersebut.

Copyable Codeblock

Salin struktur dependensi composer.json berikut ke root project AI Agent Anda untuk menginstal seluruh engine parsing dokumen sekaligus:

{
  "name": "infokoding/ai-agent-file-tools",
  "description": "Enterprise File Reader Tools (PDF, DOCX, CSV, JSON) for PHP AI Agents",
  "type": "project",
  "require": {
    "php": ">=8.2",
    "smalot/pdfparser": "^2.11",
    "phpoffice/phpword": "^1.3",
    "guzzlehttp/guzzle": "^7.9"
  },
  "autoload": {
    "psr-4": {
      "App\": "src/"
    }
  },
  "config": {
    "optimize-autoloader": true,
    "sort-packages": true,
    "allow-plugins": {}
  }
}

Composer Tip: Jalankan perintah composer install --optimize-autoloader untuk mempercepat waktu autoloading class parser pada lingkungan production.

Diagram Arsitektur: Proteksi Path Traversal & Pipeline Ekstraksi FileTool
WebP Lossless • 65 KB • Schema Ready
Diagram arsitektur keamanan FileTool AI Agent PHP: 4 tahap inspeksi keamanan mencakup basename sanitization, realpath jail check, batasan ukuran file 10MB, dan token truncation buffer sebelum masuk ke engine parsing PDF, DOCX, CSV, dan JSON
Gambar 13.1: Arsitektur 4 Lapis Keamanan FileTool PHP — Memastikan AI Agent hanya dapat membaca file di direktori sah (*Jail Directory*), mencegah kebocoran file konfigurasi sistem Linux, dan membatasi kuota token context window.

Implementasi FileTool Class untuk AI Agent PHP

Berikut adalah implementasi class FileTool berbasis ToolInterface yang mengintegrasikan 4 lapis perlindungan keamanan path traversal, pemangkasan buffer token, dan multi-format document parser:

<?php

namespace App\Tools;

class FileTool implements ToolInterface
{
    private string $allowedDir;
    private int $maxFileSizeBytes;
    private int $maxChars;

    public function __construct(
        string $allowedDir = './uploads',
        int $maxFileSizeMB = 10,
        int $maxChars = 8000
    ) {
        $this->allowedDir       = realpath($allowedDir) ?: $allowedDir;
        $this->maxFileSizeBytes = $maxFileSizeMB * 1024 * 1024;
        $this->maxChars         = $maxChars;
    }

    public function getDefinition(): array
    {
        return [
            'type'     => 'function',
            'function' => [
                'name'        => 'read_file',
                'description' => 'Membaca dan mengekstrak teks isi file lokal (PDF, TXT, CSV, JSON, DOCX). Masukkan nama file yang tersimpan di server.',
                'parameters'  => [
                    'type'       => 'object',
                    'properties' => [
                        'filename' => [
                            'type'        => 'string',
                            'description' => 'Nama file dokumen yang ingin dibaca (misal: laporan-keuangan.pdf, data.csv).',
                        ],
                    ],
                    'required' => ['filename'],
                ],
            ],
        ];
    }

    public function execute(array $args): mixed
    {
        // Lapisan Keamanan 1: Sanitasi nama file dengan basename()
        $filename = basename(trim($args['filename'] ?? ''));
        if (empty($filename)) {
            return ['status' => 'error', 'message' => 'Nama file tidak boleh kosong.'];
        }

        $filepath = $this->allowedDir . DIRECTORY_SEPARATOR . $filename;

        // Lapisan Keamanan 2: Validasi realpath terhadap direktori yang diizinkan (Jail Check)
        $realPath = realpath($filepath);
        if (!$realPath || !str_starts_with($realPath, $this->allowedDir)) {
            return ['status' => 'error', 'message' => 'Akses file ditolak: path berada di luar direktori yang diizinkan.'];
        }

        if (!file_exists($realPath) || !is_readable($realPath)) {
            return ['status' => 'error', 'message' => "File '{$filename}' tidak ditemukan atau tidak dapat dibaca."];
        }

        // Lapisan Keamanan 3: Pembatasan ukuran file fisik
        if (filesize($realPath) > $this->maxFileSizeBytes) {
            $maxMB = $this->maxFileSizeBytes / 1024 / 1024;
            return ['status' => 'error', 'message' => "Ukuran file melebihi batas maksimum {$maxMB}MB."];
        }

        $extension = strtolower(pathinfo($realPath, PATHINFO_EXTENSION));

        try {
            $text = match ($extension) {
                'txt'  => file_get_contents($realPath),
                'json' => $this->readJson($realPath),
                'csv'  => $this->readCsv($realPath),
                'pdf'  => $this->readPdf($realPath),
                'docx' => $this->readDocx($realPath),
                default => null,
            };
        } catch (\Throwable $e) {
            return ['status' => 'error', 'message' => 'Gagal memproses file: ' . $e->getMessage()];
        }

        if ($text === null) {
            return ['status' => 'error', 'message' => "Ekstensi file '.{$extension}' tidak didukung."];
        }

        // Lapisan Keamanan 4: Pemotongan buffer token teks
        $isTruncated = false;
        if (strlen($text) > $this->maxChars) {
            $text        = substr($text, 0, $this->maxChars) . "\n...[Teks terpotong karena melebihi batas token buffer]";
            $isTruncated = true;
        }

        return [
            'status'       => 'success',
            'filename'     => $filename,
            'extension'    => $extension,
            'characters'   => strlen($text),
            'is_truncated' => $isTruncated,
            'content'      => $text,
        ];
    }

    private function readJson(string $path): string
    {
        $content = file_get_contents($path);
        $data    = json_decode($content, true);
        return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
    }

    private function readCsv(string $path): string
    {
        $lines = [];
        if (($handle = fopen($path, 'r')) !== false) {
            while (($row = fgetcsv($handle, 1000, ',')) !== false) {
                $lines[] = implode(' | ', $row);
            }
            fclose($handle);
        }
        return implode("\n", $lines);
    }

    private function readPdf(string $path): string
    {
        if (!class_exists('\Smalot\PdfParser\Parser')) {
            throw new \RuntimeException('Library smalot/pdfparser belum terpasang.');
        }
        $parser = new \Smalot\PdfParser\Parser();
        $pdf    = $parser->parseFile($path);
        return $pdf->getText();
    }

    private function readDocx(string $path): string
    {
        if (!class_exists('\PhpOffice\PhpWord\IOFactory')) {
            throw new \RuntimeException('Library phpoffice/phpword belum terpasang.');
        }
        $phpWord  = \PhpOffice\PhpWord\IOFactory::load($path);
        $sections = $phpWord->getSections();
        $text     = '';
        foreach ($sections as $section) {
            foreach ($section->getElements() as $element) {
                if (method_exists($element, 'getText')) {
                    $text .= $element->getText() . "\n";
                }
            }
        }
        return $text;
    }
}
Poin Kunci Definisi Class FileTool (AEO Direct Answer):
  • Kontrak ToolInterface: Mengimplementasikan method getDefinition() untuk JSON Schema dan execute() untuk ekstraksi runtime lokal.
  • Jail Directory Defense: Menerapkan basename() dan realpath() untuk mengunci pembacaan file hanya pada folder $allowedDir (Anti-Path Traversal).
  • Format Terdukung: Parsing native dokumen PDF (vektor layer), DOCX (Word Section), CSV, JSON terformat, dan plain TXT.
  • Buffer Token Safety: Memotong konten teks pada batas $maxChars (default: 8.000 karakter) guna mencegah token blowout di context window LLM.
  • Graceful Error Handling: Mengembalikan payload terstruktur ['status' => 'error', 'message' => '...'] saat file korup atau tidak ditemukan.

Bedah Mekanisme Keamanan Path Traversal & Error Handling:

1. Sanitasi basename() & Jail Directory: Penggunaan basename() secara otomatis melucuti manipulasi path seperti ../../etc/passwd atau ..\..\Windows\win.ini. Selanjutnya, validasi str_starts_with(realpath($filepath), $this->allowedDir) memastikan file target benar-benar berada di dalam folder yang diizinkan tanpa celah symlink bypass.

2. Pembatasan Memori & Truncation Token: File dokumen berukuran puluhan megabyte dapat memicu kehabisan memori server (*Memory Exhaustion*). FileTool menolak file di atas 10 MB dan memotong string teks pada batas $maxChars (default: 8.000 karakter) agar tidak menghabiskan kuota context window model AI.

3. Graceful Error Serialization: Seluruh eksepsi parsing (misal: PDF terenkripsi password atau format DOCX korup) ditangkap dalam blok try-catch dan dikembalikan sebagai payload JSON terstruktur status: error, menjaga agen AI tetap berjalan tanpa fatal crash.

Integrasi FileTool ke dalam AI Agent PHP Loop

Berikut adalah contoh skrip eksekusi pengujian membaca laporan penjualan PDF:

<?php
// run-agent-file.php

require_once __DIR__ . '/vendor/autoload.php';

use App\OpenAIClient;
use App\Agent\Agent;
use App\Tools\FileTool;

$client = new OpenAIClient(getenv('OPENAI_API_KEY'));
$agent  = new Agent($client, maxIterations: 5);

// Registrasikan FileTool dengan direktori uploads
$agent->registerTool(new FileTool(allowedDir: __DIR__ . '/uploads', maxFileSizeMB: 10, maxChars: 8000));

// Instruksi membaca dokumen
$prompt = 'Baca file "laporan-penjualan-q3.pdf", kemudian buatkan ringkasan 3 poin utama keuntungan perusahaan.';
$response = $agent->run($prompt);

echo "Hasil Analisis AI Agent:\n" . $response . "\n";

Penanganan Dokumen Besar (>10MB): Direct FileTool vs Streaming Chunking RAG

Membaca dokumen masif berukuran puluhan megabyte (seperti laporan tahunan 200 halaman atau log server 50MB) secara utuh ke dalam memori PHP akan memicu batas memory_limit dan melampaui kapasitas context window model AI. Berikut adalah strategi perbandingan arsitektural untuk menangani file besar:

Dimensi Penanganan Direct FileTool (In-Memory Buffer) Streaming & Chunking RAG (Enterprise)
Ukuran Dokumen IdealFile kecil hingga menengah (<10 MB, <20 halaman)File raksasa (>10 MB – 500 MB, ratusan halaman)
Metode Konsumsi MemoriMemuat teks utuh ke RAM (rentan memory_limit jika tidak dibatasi)Membaca secara stream baris per baris via generator PHP
Penggunaan Token LLMMengirim ribuan kata sekaligus (berisiko token blowout)Hanya mengirim 3–5 chunk paling relevan via Vector Search
Waktu Latensi ResponsSangat cepat (<200ms) untuk file ringkasMemerlukan tahap pra-indeks (Embedding Vector Database)
Ketepatan KonteksCocok untuk membaca dokumen tunggal secara menyeluruhSangat akurat untuk Q&A spesifik pada ribuan halaman arsip

Pola Implementasi Generator Stream di PHP:

Untuk file CSV/TXT besar, gunakan generator yield untuk memecah teks per 1.000 karakter (*sliding window chunking*) dengan overlap 100 karakter sebelum di-generate embedding-nya ke pgvector / Qdrant.

RAG Strategy Takeaway: Gunakan Direct FileTool untuk interaksi dokumen instan, dan alihkan ke pipeline RAG Chunking jika dokumen melebihi kuota 10 MB.

FAQ: Pertanyaan Seputar Tool Membaca File AI Agent PHP

1. Bagaimana cara mencegah serangan Path Traversal pada FileTool?

Ringkasan Jawaban:Gunakan fungsi basename() untuk mengekstrak murni nama file dan verifikasi path absolut menggunakan realpath() untuk memastikan file berada di dalam direktori $allowedDir.

Ini mencegah penyerang menyisipkan karakter dot-dot-slash (../../) untuk membaca file sensitif sistem seperti .env atau /etc/shadow.

2. Mengapa teks file perlu dipotong (truncation) sebelum dikirimkan ke AI?

Ringkasan Jawaban:Memotong teks pada batas aman (misal: 8.000 karakter) mencegah lonjakan konsumsi token (*token blowout*) dan menghindari error batas maksimal context window LLM.

Untuk dokumen sangat panjang (ratusan halaman), pertimbangkan penerapan teknik chunking dan vector embeddings (RAG) pada bab lanjutan.

3. Library apa yang direkomendasikan untuk membaca PDF dan DOCX di PHP?

Ringkasan Jawaban:Gunakan smalot/pdfparser untuk ekstraksi teks dokumen PDF dan phpoffice/phpword untuk membaca dokumen Microsoft Word DOCX.

Kedua library ini berjalan murni di PHP tanpa memerlukan ekstensi biner tambahan atau instalasi utilitas CLI eksternal.

RA

Rusmawan Abdullah Sani

Lead Software Engineer & System Architect

Praktisi pengembangan backend PHP modern, arsitektur AI Agent, microservices, dan otomasi server Linux. Terhubung melalui profil LinkedIn.