Skip to content

List Converter — PHP source

Transform a list between separators (newline, comma, space, pipe, semicolon, tab) with trim, dedupe, sort, and empty-removal options. Runs entirely in your browser, with a shareable link.

This is the PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.

<?php
/**
 * list-converter — convert lists between separators (newline/comma/space/pipe/...).
 *
 * Language: PHP (8.1+, standard library only — mbstring used for safe
 *           multibyte handling, which is effectively universal in modern PHP)
 * Source:   CosmoDev polyglot showcase port of the List Converter tool, ported
 *           from cli/list-converter/list-converter.go (the Go CLI twin), which
 *           is itself the lock-step mirror of src/lib/list-converter.ts (the
 *           canonical TypeScript implementation).
 * License:  display source — part of CosmoDev's polyglot tool pages.
 *
 * Design goals:
 *   - Pure + deterministic; never throws.
 *   - Functionally equivalent to the Go twin: same inputs -> same outputs.
 *   - Self-contained: stdlib only (no Composer packages).
 *
 * Pipeline: split on the "from" separator -> (trim each item) -> (drop empties)
 * -> (dedup, optionally case-insensitive, first occurrence wins) -> (stable
 * sort, optionally case-insensitive) -> join with the "to" separator. The
 * Separator enum and options mirror the Go types exactly; defaults are
 * from=newline, to=comma (the TS resolveSep fallbacks).
 */

declare(strict_types=1);

namespace CosmoDev\ListConverter;

/**
 * A built-in list separator. Mirrors the Go twin's Separator enum; the default
 * NEWLINE matches the TS "from" default. Backed by the literal separator
 * string so it doubles as the split/join glue (PHP enums can't be zero-valued
 * integers with a meaningful char, so a string-backed enum is the clean fit).
 */
enum Separator: string
{
    case Newline = "\n";
    case Comma = ',';
    case Space = ' ';
    case Pipe = '|';
    case Semicolon = ';';
    case Tab = "\t";

    /**
     * Resolve by the TS-style lowercase name ("comma", "newline", ...).
     * Provided for API symmetry with the TS source; callers normally pass the
     * enum case directly.
     */
    public static function fromName(string $name): self
    {
        return match ($name) {
            'newline'   => self::Newline,
            'comma'     => self::Comma,
            'space'     => self::Space,
            'pipe'      => self::Pipe,
            'semicolon' => self::Semicolon,
            'tab'       => self::Tab,
            default     => self::from($name),
        };
    }
}

/**
 * Options for convert(). Each key is optional; defaults match the Go twin.
 *
 *   - from:             Separator  default Newline  source separator
 *   - to:               Separator  default Comma    target separator
 *   - trim:             bool       default false    trim each split item
 *   - removeEmpty:      bool       default false    drop items equal to ''
 *   - unique:           bool       default false    keep first occurrence only
 *   - sort:             bool       default false    stable-sort ascending
 *   - caseInsensitive:  bool       default false    lowercase keys for unique/sort
 */
function listconverter_default_options(): array
{
    return [
        'from'            => Separator::Newline,
        'to'              => Separator::Comma,
        'trim'            => false,
        'removeEmpty'     => false,
        'unique'          => false,
        'sort'            => false,
        'caseInsensitive' => false,
    ];
}

/**
 * Lowercase a string with the same observable effect as Go's strings.ToLower
 * for the ASCII range this tool targets. mb_strtolower handles multibyte input
 * safely.
 */
function listconverter_lower(string $s): string
{
    return mb_strtolower($s, 'UTF-8');
}

/**
 * Convert a list between separators. Never throws; an empty $input simply
 * yields a one-item list joined back together (mirroring Go's
 * strings.Split("", sep) == [""]).
 */
function listconvert(string $input, array $options = []): string
{
    $options = array_merge(listconverter_default_options(), $options);

    $from = $options['from'] instanceof Separator ? $options['from']->value : (string) $options['from'];
    $to   = $options['to'] instanceof Separator ? $options['to']->value : (string) $options['to'];

    $items = explode($from, $input);

    if (!empty($options['trim'])) {
        $items = array_map('trim', $items);
    }
    if (!empty($options['removeEmpty'])) {
        $items = array_values(array_filter($items, fn ($s) => $s !== ''));
    }
    if (!empty($options['unique'])) {
        // Keyed by the (optionally lowercased) value; the first-seen item
        // survives with its original casing. PHP preserves insertion order
        // across array keys, so one pass dedups and keeps order.
        $seen = [];
        foreach ($items as $s) {
            $key = !empty($options['caseInsensitive']) ? listconverter_lower($s) : $s;
            if (!array_key_exists($key, $seen)) {
                $seen[$key] = $s;
            }
        }
        $items = array_values($seen);
    }
    if (!empty($options['sort'])) {
        // PHP's sort isn't stable across all versions, but usort has been
        // stable since 8.0. We lowercase only for comparison when
        // caseInsensitive is set, leaving the stored values untouched.
        $ci = !empty($options['caseInsensitive']);
        usort($items, function (string $a, string $b) use ($ci): int {
            $ka = $ci ? listconverter_lower($a) : $a;
            $kb = $ci ? listconverter_lower($b) : $b;
            return $ka <=> $kb;
        });
    }

    return implode($to, $items);
}

/**
 * Convenience wrapper using default options — the common "newline list to a
 * comma list" case (matches the tool's default From/To).
 */
function listconvert_default(string $input): string
{
    return listconvert($input, listconverter_default_options());
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →