Weave 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

Phpepub Bundle Laravel Package

rampmaster/phpepub-bundle

A PHP bundle for generating EPUB ebooks from HTML content. It offers a straightforward API for assembling chapters, managing metadata and assets, and adding ebook export to Laravel or PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle wrapping the grandt/phpepub library — not a native Laravel package. That distinction shapes everything below.

Install:

composer require rampmaster/phpepub-bundle

Where to look first:

  1. The bundle class (*Bundle.php) and its DI extension — this is where the PHPePub service is registered. Read it to see exactly which constructor arguments and config keys are exposed.
  2. The underlying grandt/phpepub API (EPUB, Chapter, setIdentifier, addChapter, finalize) — you will spend most of your time here, not in the bundle.
  3. composer.json of the bundle for the PHP version constraint and the pinned grandt/phpepub version.

Reality check for Laravel: Laravel's package discovery only reads the extra.laravel.providers key. A Symfony bundle has no such key, so nothing is auto-registered. Do not expect app(PHPePub::class) to resolve after composer require.

First use case — a minimal "export to EPUB" endpoint, skipping the bundle's container wiring and instantiating the library directly:

use Grandt\PHPePub\EPUB;

Route::get('/books/{book}/epub', function (Book $book) {
    $epub = new EPUB($book->title, $book->slug, $book->author);
    $epub->setLanguage('en');
    $epub->setIdentifier($book->isbn ?? 'urn:uuid:' . Str::uuid(), 'uuid');

    foreach ($book->chapters as $i => $chapter) {
        $epub->addChapter(
            "Chapter " . ($i + 1),
            $chapter->slug . '.xhtml',
            view('epub.chapter', ['chapter' => $chapter])->render()
        );
    }

    return response()->streamDownload(function () use ($epub) {
        $epub->finalize();
        echo $epub->getBook();
    }, Str::slug($book->title) . '.epub');
});

Treat the bundle itself as an optional convenience: if you are not on Symfony, you get more value from the transitive dependency than from the bundle wrapper.

Implementation Patterns

Wire it up yourself, deliberately. If you want container parity with the Symfony original, write a small Laravel service provider rather than trying to reuse the bundle class:

class EpubServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(EpubFactory::class, fn () => new EpubFactory(
            config('epub.base_path'),
            config('epub.temp_path')
        ));
    }
}

Then your EpubFactory composes EPUB and centralises the parts every book needs (language, identifier scheme, stylesheet, cover injection).

Model a "book builder" abstraction. Don't scatter addChapter() calls across controllers. Give yourself a class that accepts an iterable of domain objects and emits a finished EPUB:

class BookBuilder
{
    public function build(Book $book): string
    {
        $epub = new EPUB($book->title, $book->slug, $book->author->name);
        $epub->setLanguage($book->locale);

        foreach ($book->chapters as $chapter) {
            $this->addChapterWithImages($epub, $chapter);
            $epub->addChapter(...);
        }

        $epub->finalize();

        return $epub->getBook(); // binary string
    }
}

Queue it for anything non-trivial. EPUB assembly is CPU- and memory-bound (string concatenation plus a zip pass). For books over a few MB, move it to a job that writes the .epub to your storage disk and emails a signed URL:

GenerateBookEpub::dispatch($book)->onQueue('exports');
// job: Storage::disk('s3')->put($path, $builder->build($book))

Separate the view layer from the EPUB. Render chapters through Blade partials using XHTML-safe markup, then feed the string to addChapter(). This keeps your templates testable and lets the same partials power both web and EPUB output. Keep a dedicated resources/views/epub/ tree so you can strip navigation and scripts.

Integration tips:

  • Expose the feature as an artisan command (php artisan epub:build {book}) for reprocessing after content edits.
  • Cache the generated file keyed by a content hash — regenerate only when chapters change.
  • Validate output in a test using an EPUB checker (e.g. epubcheck in CI) against a fixture book, not with string assertions.

Gotchas and Tips

The Symfony/Laravel container mismatch is the headline pitfall. The bundle's extension is built around Symfony's ContainerBuilder and Extension interface. Laravel's Illuminate\Container\Container is not a Symfony\Component\DependencyInjection\ContainerBuilder. Calling build() on the bundle in a Laravel app will not do what you expect. Either (a) use the library directly, or (b) re-register the services manually in a Laravel provider. Do not assume the bundle "just works" on Laravel because it installs cleanly.

Version 1.0.0 is a single squashed PR from a first-time bot contributor. There is no track record, no test matrix to speak of, no deprecation history. Pin the version in composer.json and re-verify EPUB output after any bump. The changelog is one line — plan to read the diff, not the notes.

Filesystem paths, not URLs. PHPePub resolves images and addFile()/cover references against the real filesystem. Passing https://cdn.example.com/cover.jpg or Storage::url(...) will fail at zip time, often with a confusing silent skip rather than an exception. Resolve to absolute local paths first:

$localCover = Storage::disk('local')->path('covers/'.$book->cover);
$epub->setCoverImage($localCover, 'cover.jpg');

getBook() is a one-shot. After finalize(), calling finalize() again or reusing the EPUB instance produces corrupt or empty archives. Build once, store the binary, and never hold instances across requests.

Temp directory permissions bite in containers. EPUB assembly writes scratch files. Ensure both your storage/app/tmp path and the process user's sys_get_temp_dir() are writable — in a read-only container filesystem this is the usual failure mode on production deployments. Override the temp path explicitly rather than relying on the default.

Memory scales with total book size. Each chapter is held as a string before the zip pass. A 50 MB image-heavy book will inflate far beyond 50 MB of PHP memory. Set a ceiling in your job (memory_limit, and a size guard before dispatch) and prefer generating per-volume rather than per-library.

XHTML is stricter than HTML. Unclosed <img>, <br> without a slash, and unescaped ampersands all produce EPUBs that readers silently reject or render oddly. Run your Blade partials through a strict XHTML pass in tests.

Extension points are thin. The bundle exposes little configuration beyond what the underlying library offers. If you need custom OPF metadata, custom spine ordering, or NCX tuning, you are working against grandt/phpepub directly — document that boundary for your team so nobody hunts through the bundle for a knob that does not exist.

Debugging strategy: when a reader rejects a file, unzip it (unzip -l book.epub) and inspect META-INF/container.xml, OEBPS/content.opf, and the spine order. Nearly every "it doesn't open" report traces to a missing manifest item or an absolute path leaking into content.opf. Keep a small epub:inspect command that dumps the manifest for support cases.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package