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

Zend Text Laravel Package

zendframework/zend-text

zendframework/zend-text is a legacy Zend Framework component for text manipulation and presentation. It renders ASCII tables and FIGlet banners, wraps, aligns, and truncates strings, and provides multibyte-safe helpers for CLI and web output.

View on GitHub
Deep Wiki
Context7

Getting Started

Know what this is before you install it. zendframework/zend-text is the old Zend Framework component that does exactly two things: render text as FIGlet ASCII-art banners (Zend\Text\Figlet\Figlet) and render arrays of data as fixed-width text tables (Zend\Text\Table\Table). There is no Laravel integration, no facade, no config file, and zero dependents on Packagist — you are wiring it up yourself.

Install. The 2019 release declares php: ^5.6 || ^7.0, so on a modern Laravel/PHP 8+ app a plain composer require will fail platform checks. Two options:

# Option A (pragmatic, still archived):
composer require zendframework/zend-text --ignore-platform-req=php

# Option B (recommended): use the maintained fork, same API, different namespace
composer require laminas/laminas-text

If you take Option B, every code sample below works by replacing Zend\Text with Laminas\Text. Nothing else changes.

Where to look first.

  • src/Figlet/Figlet.php — constructor options array, render(), font handling.
  • src/Table/Table.php — columnWidths, decorator, autoSeparate, appendRow().
  • src/Table/Decorator/Ascii.php and Unicode.php — the box-drawing character sets.
  • resources/fonts/*.flf — the FIGlet fonts shipped with the package. These are the whole reason to use the component; the renderer itself is thin.

First use case — a banner in an Artisan command:

use Zend\Text\Figlet\Figlet;

class BannerCommand extends Command
{
    protected $signature = 'app:banner {text=Deploying}';

    public function handle()
    {
        $figlet = new Figlet(['outputWidth' => 100]);
        $this->line($figlet->render($this->argument('text')));
    }
}

That is the 80% case. The 20% case is Table inside a report command, which is covered next.

Implementation Patterns

Bind the renderers as singletons, not per-call. Figlet parses a .flf file on setFont(), and Table re-does layout work on every render. In a long-running queue worker or an Octane process, construct once and reuse:

// app/Providers/AppServiceProvider.php
$this->app->singleton(Figlet::class, fn () => new Figlet([
    'font'         => base_path('resources/figlet/big.flf'),
    'outputWidth'  => (int) config('text.width', 100),
    'handleParagraphs' => true,
]));

Keep the config values in config/text.php so width can differ between local (narrow terminal) and CI (wide).

Wrap the dependency behind one of your own classes. This is the single most important pattern given the package is archived. Isolate it so swapping to Laminas — or to a maintained alternative — is a one-file change:

namespace App\Support;

use Zend\Text\Table\Table;

class TextTable
{
    public static function fromRows(array $headers, array $rows): string
    {
        $table = new Table([
            'decorator'    => Table::DECORATOR_ASCII,
            'autoSeparate' => Table::AUTO_SEPARATE_ROW,
        ]);
        $table->appendRow($headers);
        foreach ($rows as $row) {
            $table->appendRow(array_map('strval', $row));
        }
        return rtrim((string) $table, "\n");
    }
}

The rtrim matters — Table::__toString() and Figlet::render() both leave a trailing newline, and Laravel's $this->line() adds another.

Table building from Eloquent. The natural Laravel workflow is mapping a collection to rows, then rendering for a CLI report or a plain-text email body:

$rows = Order::query()->latest()->limit(20)->get()
    ->map(fn ($o) => [$o->id, $o->customer_email, number_format($o->total, 2)])
    ->all();

$this->line(TextTable::fromRows(['ID', 'Customer', 'Total'], $rows));

Set columnWidths explicitly when you have a long free-text column; otherwise the table expands to the widest cell and blows past terminal width:

new Table(['columnWidths' => [8, 40, 12], 'decorator' => Table::DECORATOR_UNICODE]);

Long cell content is wrapped inside the column width, and embedded \n in a cell starts a new physical line within that row, so multi-line descriptions work without extra effort.

Per-column and per-row alignment. Global alignment lives on Column; row-level overrides use Row:

use Zend\Text\Table\Row;
use Zend\Text\Table\Column;
use Zend\Text\Table\Table;

$table = new Table(['columnWidths' => [20, 10]]);
$table->appendRow(['Total', '1,240.00']);

$row = new Row(['align' => Table::ALIGN_RIGHT]);
$row->appendColumn(new Column('Grand total'));
$row->appendColumn(new Column('99,999.00'));
$table->appendRow($row);

Rendering into HTML mail. Because this is plain text, it belongs inside <pre> in a Blade mail view, escaped:

<pre style="font-family: monospace">{{ $table }}</pre>

Do not pass the output through nl2br() — that destroys the column alignment.

FIGlet justification as a cheap "header" pattern. Figlet::JUSTIFICATION_CENTER plus outputWidth matching the terminal width gives you a banner that adapts between a 80-col CI log and a 140-col laptop terminal, driven by config('text.width') or Termwind/exec('tput cols') if you want to be fancy.

Gotchas and Tips

The package is archived — treat it as a vendored dependency, not a live one. No security fixes, no PHP 8.3/8.4 compatibility work. laminas/laminas-text is the drop-in successor with the same class shapes; migrate while your wrapper class is still small. Pin the exact version in composer.json and do not run composer update on it casually.

Platform requirement blocks install on PHP 8. Even with --ignore-platform-req=php, check that the .flf parser and MultiByte helpers behave — Zend\Text\MultiByte falls back to iconv if mbstring is missing, and the fallbacks were written for PHP 5 semantics. Ensure ext-mbstring is installed; without it the Unicode table decorator mis-measures double-width CJK and emoji cells and the borders drift.

Font resolution is by name, not path, unless you give it a path. new Figlet(['font' => 'big']) looks in the package's resources/fonts directory. new Figlet(['font' => '/abs/path/custom.flf']) loads your own. A wrong value throws Zend\Text\Figlet\Exception\RuntimeException with a "font not found" message — the exception does not echo the resolved path, so dd() the option when debugging.

outputWidth does not mean "don't exceed this". It controls the smushing/wrapping budget, and a single wide character glyph can still push a line over. If output must fit a hard budget (e.g. a Slack webhook or a fixed-width PDF), assert on max(array_map('strlen', explode("\n", $rendered))) in a test rather than trusting the setting.

Empty and null cells. appendRow() expects scalar-convertible values and will throw on arrays or objects. Cast null to '' and enums to ->value before handing rows to the table; array_map('strval', ...) handles most of it but strval(null) is '' only by accident of PHP coercion — be explicit.

Column-count mismatch is a hard error. Every row must have exactly as many cells as columnWidths (or as the first row, if widths are inferred). Adding a column to your query mid-refactor produces InvalidArgumentException at render time, not at appendRow() time — so failures surface far from the cause. Build rows through one helper that validates counts.

Unicode decorator vs. CI logs. Table::DECORATOR_UNICODE looks great locally and turns into mojibake in Jenkins, Docker json-file logs, or any pipeline with LANG=C. Default to DECORATOR_ASCII in CI and select via an env flag; don't hardcode Unicode.

Output is a string, always. Table implements __toString(); Figlet does not — calling echo $figlet or interpolating it into a string silently prints nothing useful or errors. Always call ->render() on Figlet.

Testing: assert on the full rendered string with a nowdoc heredoc for small fixtures. Snapshot tests are brittle across widths, so parameterize the width in tests rather than copying 100-column blobs. Strip trailing whitespace on each line before comparing — both decorators pad cells, and editors/CI often trim it.

Extension points if you must customize: implement Zend\Text\Table\Decorator\Decorator for a brand-new border style (pass an instance as the decorator option), and subclass Table to override _calculateColumnWidths() if you need weighted or percentage-based widths. Both are the only two seams the component really offers; everything else is private state.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo