<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Artur</title>
    <description>The latest articles on DEV Community by Artur (@aheinze).</description>
    <link>https://dev.to/aheinze</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3810906%2F3c293e1d-5342-4198-8c08-fceca4fb8452.jpeg</url>
      <title>DEV Community: Artur</title>
      <link>https://dev.to/aheinze</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aheinze"/>
    <language>en</language>
    <item>
      <title>Carelo: A Modern Dual-Pane File Manager for Linux</title>
      <dc:creator>Artur</dc:creator>
      <pubDate>Sun, 24 May 2026 04:13:10 +0000</pubDate>
      <link>https://dev.to/aheinze/carelo-a-modern-dual-pane-file-manager-for-linux-39c7</link>
      <guid>https://dev.to/aheinze/carelo-a-modern-dual-pane-file-manager-for-linux-39c7</guid>
      <description>&lt;p&gt;Moving from macOS to Linux is usually easier than people expect. The terminal is excellent, package managers are powerful, window managers are flexible, and most development workflows feel at home quickly.&lt;/p&gt;

&lt;p&gt;Then you start looking for a file manager.&lt;/p&gt;

&lt;p&gt;Not just a basic file browser. A real dual-pane file manager. Something that lets you compare folders, move files with confidence, work with remote storage, preview content, open a terminal when needed, and keep your hands on the keyboard. Something modern enough to feel like it belongs on a current desktop, but practical enough to handle daily work.&lt;/p&gt;

&lt;p&gt;On macOS, ForkLift fills that role well for me. It is polished, fast, and built around the kind of workflows power users actually repeat every day. After using that kind of tool, switching to Linux can feel surprisingly rough. Linux has capable file managers, and some classic dual-pane tools are extremely powerful, but the choices often fall into two categories: beautiful single-pane desktop browsers, or older commander-style tools that prioritize capability over modern interaction design.&lt;/p&gt;

&lt;p&gt;Carelo started from that gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;A dual-pane file manager is not just about showing two folders side by side. That is the easy part.&lt;/p&gt;

&lt;p&gt;The real value is flow.&lt;/p&gt;

&lt;p&gt;You want to keep a project folder on the left and a build output folder on the right. You want to copy assets from Downloads into a repo, inspect a PDF, rename a file, archive a folder, jump into a remote server, compare locations, and keep moving. You want drag and drop when it is faster, keyboard shortcuts when they are faster, and a preview panel when opening another app would break focus.&lt;/p&gt;

&lt;p&gt;Linux has tools that cover pieces of this. Nautilus integrates well with GNOME. Dolphin is mature and feature-rich. Midnight Commander is still great in the terminal. Krusader is powerful. But if you are coming from a polished macOS dual-pane workflow, the overall experience can still feel fragmented.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; is an attempt to build the file manager I wanted after moving from macOS to Linux: modern, local-first, dual-pane by default, and designed for everyday file work rather than nostalgia.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Carelo Is
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; is a desktop file manager built with Tauri and Rust. It focuses on fast local file operations, dual-pane browsing, rich previews, remote storage, archives, and configurable tools.&lt;/p&gt;

&lt;p&gt;The core idea is simple: keep the interface familiar, but make the workflow feel current.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; is not trying to hide the filesystem or turn file management into a cloud dashboard. It is for people who know where their files live and want a better way to work with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dual Panes, Tabs, and Views
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; starts with the dual-pane model because that is still the most efficient layout for many real tasks. Copying, moving, comparing, extracting, staging, and organizing files all benefit from seeing source and destination at the same time.&lt;/p&gt;

&lt;p&gt;Each pane can have its own tabs, so you can keep multiple working locations open without losing context. The app also supports list, grid, and column views, making it possible to switch between dense file operations and more visual browsing depending on the folder.&lt;/p&gt;

&lt;p&gt;The goal is not to force one workflow. It is to make common workflows cheap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preview Without Losing Context
&lt;/h2&gt;

&lt;p&gt;One of the biggest workflow breaks in file management is opening files just to know what they are.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; includes a preview panel for metadata and common media types. Images, audio, video, PDFs, and text-like content can be inspected without leaving the file manager. The preview panel also shows practical file details such as size, timestamps, owner, group, permissions, and path information.&lt;/p&gt;

&lt;p&gt;This matters more than it sounds. When you are cleaning up downloads, checking exported assets, reviewing documents, or comparing similar files, preview speed directly affects how focused the work feels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search That Fits File Work
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; has two search modes.&lt;/p&gt;

&lt;p&gt;Fuzzy file search helps jump to files and folders by name. It is built for quick navigation inside the current location and streams partial results while scanning. Content search looks inside files, supports plain text streaming, and can search extracted text from formats such as PDFs and Office-style documents where supported.&lt;/p&gt;

&lt;p&gt;Both search paths are cancellable and report progress through the current work indicator. That matters on large folders and remote locations, where a search should never make the app feel stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote Storage
&lt;/h2&gt;

&lt;p&gt;A Linux file manager should not stop at the local disk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; supports remote volumes such as SFTP, FTP, SMB/CIFS, WebDAV, and S3-compatible storage through its remote storage layer. Remote locations appear in the sidebar, can be browsed in panes, and participate in normal file workflows where possible.&lt;/p&gt;

&lt;p&gt;The goal is to make a remote server or NAS feel like part of the same working environment, without forcing every operation through a separate app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Archives as Part of the Workflow
&lt;/h2&gt;

&lt;p&gt;Archives are part of file management, not a separate chore.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; supports browsing and extracting archives, along with archive creation for common formats including ZIP, TAR, TAR.GZ, TAR.ZST, and 7Z. Long-running archive operations show progress and can be cancelled.&lt;/p&gt;

&lt;p&gt;That makes archives behave more like file operations and less like modal interruptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Tools and Editor Integration
&lt;/h2&gt;

&lt;p&gt;Power users always have their own tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; includes configurable context-menu tools, so commands such as opening a path in an editor can be added without changing the app. Tool commands can use placeholders like the selected path, and availability can be scoped to files, folders, or specific extensions.&lt;/p&gt;

&lt;p&gt;The point is not to guess everyone’s setup. It is to make the setup configurable enough that Carelo can fit into yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Just Use the Existing Tools?
&lt;/h2&gt;

&lt;p&gt;You should, if they work for you.&lt;/p&gt;

&lt;p&gt;Dolphin, Nautilus, Krusader, Double Commander, Midnight Commander, and others all have strengths. Carelo exists because none of them quite matched the experience I wanted after moving from ForkLift to Linux.&lt;/p&gt;

&lt;p&gt;The missing piece was not one checkbox feature. It was the combination:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dual-pane browsing as the default workflow&lt;/li&gt;
&lt;li&gt;modern interface and previews&lt;/li&gt;
&lt;li&gt;remote storage in the same mental model as local files&lt;/li&gt;
&lt;li&gt;archive operations with progress and cancellation&lt;/li&gt;
&lt;li&gt;configurable external tools&lt;/li&gt;
&lt;li&gt;fast fuzzy search and content search&lt;/li&gt;
&lt;li&gt;enough keyboard and mouse support to move naturally between both&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; is built around that combination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Carelo Is For
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; is for developers, designers, sysadmins, content workers, and Linux desktop users who spend real time moving through folders.&lt;/p&gt;

&lt;p&gt;It is for people who want more than a simple file browser, but do not want to live entirely inside a terminal file manager.&lt;/p&gt;

&lt;p&gt;It is for people who miss the feel of tools like ForkLift, but want something that fits a Linux-first workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Direction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; is still evolving, and that is part of the point. File managers are deeply personal tools. The only way to make one good is to use it, hit the rough edges, and keep sanding them down.&lt;/p&gt;

&lt;p&gt;The direction is clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep local file operations fast and reliable&lt;/li&gt;
&lt;li&gt;make remote storage feel first-class&lt;/li&gt;
&lt;li&gt;improve previews and metadata&lt;/li&gt;
&lt;li&gt;expand search and indexing without making the app heavy&lt;/li&gt;
&lt;li&gt;keep power-user customization simple&lt;/li&gt;
&lt;li&gt;preserve the dual-pane workflow as the center of the product&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Linux deserves modern file management tools that are not just ports, clones, or nostalgia projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;Carelo&lt;/a&gt; is one attempt to build that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aheinze/Carelo" rel="noopener noreferrer"&gt;https://github.com/aheinze/Carelo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg8fzjhnwd8o4mvnicfd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg8fzjhnwd8o4mvnicfd.png" alt="Carelo Screenshot" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>tooling</category>
      <category>software</category>
      <category>productivity</category>
    </item>
    <item>
      <title>ScriptLite — a sandboxed ECMAScript subset interpreter for PHP (with optional C extension)</title>
      <dc:creator>Artur</dc:creator>
      <pubDate>Sat, 07 Mar 2026 03:50:47 +0000</pubDate>
      <link>https://dev.to/aheinze/scriptlite-a-sandboxed-ecmascript-subset-interpreter-for-php-with-optional-c-extension-58ea</link>
      <guid>https://dev.to/aheinze/scriptlite-a-sandboxed-ecmascript-subset-interpreter-for-php-with-optional-c-extension-58ea</guid>
      <description>&lt;p&gt;I've been working on &lt;a href="https://github.com/Cockpit-HQ/Cockpit" rel="noopener noreferrer"&gt;Cockpit&lt;/a&gt;, a headless CMS, for a while now. One thing that kept coming up was the need for user-defined logic — computed fields, validation rules, content transformations, stuff like that. The kind of thing where you want your CMS users to write small snippets of logic without giving them the keys to the entire PHP runtime.&lt;/p&gt;

&lt;p&gt;I looked at existing options. V8js is heavy and a pain to deploy. Lua doesn't feel right for a web-focused CMS where most users already know JavaScript. Expression languages are too limited once you need a loop or a callback. So I started building my own.&lt;/p&gt;

&lt;p&gt;What began as a simple expression evaluator for Cockpit turned into a full ECMAScript subset interpreter: &lt;a href="https://github.com/aheinze/ScriptLite" rel="noopener noreferrer"&gt;ScriptLite&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it does
&lt;/h3&gt;

&lt;p&gt;It runs JavaScript (ES5/ES6 subset) inside PHP. No filesystem access, no network, no &lt;code&gt;eval&lt;/code&gt;, no &lt;code&gt;require&lt;/code&gt; — scripts can only touch the data you explicitly pass in. Think of it as a sandbox where users write logic and you control exactly what they can see and do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ScriptLite\Engine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// User-defined pricing rule stored in your database&lt;/span&gt;
&lt;span class="nv"&gt;$rule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'
    let total = items.reduce((sum, item) =&amp;gt; sum + item.price * item.qty, 0);
    if (total &amp;gt; 100) total *= (1 - discount);
    Math.round(total * 100) / 100;
'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'items'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;29.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'qty'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;49.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'qty'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'discount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;// $result === 98.97&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It supports the stuff people actually use day to day: arrow functions, destructuring, template literals, spread/rest, array methods (&lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;reduce&lt;/code&gt;, ...), object methods, regex, try/catch, &lt;code&gt;Math&lt;/code&gt;, &lt;code&gt;JSON&lt;/code&gt;, &lt;code&gt;Date&lt;/code&gt;, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP interop
&lt;/h3&gt;

&lt;p&gt;You can pass in PHP objects directly. Scripts can read properties, call methods, and mutations flow back to your PHP side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'
    if (order.total() &amp;gt; 500) {
        order.applyDiscount(10);
        order.setStatus("vip");
    }
'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'order'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// $order-&amp;gt;status is now "vip"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also pass PHP closures as callable functions, so you control exactly what capabilities the script has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'
    let users = fetchUsers();
    let active = users.filter(u =&amp;gt; u.lastLogin &amp;gt; cutoff);
    active.map(u =&amp;gt; u.email);
'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'fetchUsers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$userRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s1"&gt;'cutoff'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;strtotime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-30 days'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Three execution backends
&lt;/h3&gt;

&lt;p&gt;This is the part that got a bit out of hand. I ended up building three backends:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bytecode VM&lt;/strong&gt; — compiles to bytecode, runs on a stack-based VM in pure PHP. Works everywhere, no dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PHP transpiler&lt;/strong&gt; — translates the JavaScript to PHP source code that OPcache/JIT can optimize. About 40x faster than the VM. Good for hot paths.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;C extension&lt;/strong&gt; — a native bytecode VM with computed-goto dispatch. About 180x faster than the PHP VM. Because at some point I thought "how fast can this actually go" and couldn't stop.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The nice thing is that the API is the same regardless of backend. The engine picks the fastest available one automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;       &lt;span class="c1"&gt;// uses C ext if loaded, else PHP VM&lt;/span&gt;
&lt;span class="nv"&gt;$engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// force pure PHP&lt;/span&gt;

&lt;span class="c1"&gt;// Same code, same results, different speed&lt;/span&gt;
&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'items.filter(x =&amp;gt; x &amp;gt; 3)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'items'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The transpiler path is interesting if you want near-native speed without a C extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Transpile once, run many times with different data&lt;/span&gt;
&lt;span class="nv"&gt;$callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTranspiledCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'config'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$batch1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'config'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$cfg&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$batch2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'config'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$cfg&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Possible use cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User-defined formulas&lt;/strong&gt; — let users write &lt;code&gt;price * quantity * (1 - discount)&lt;/code&gt; in a CMS, form builder, or spreadsheet-like app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation rules&lt;/strong&gt; — store rules like &lt;code&gt;value.length &amp;gt; 0 &amp;amp;&amp;amp; value.length &amp;lt;= 280&lt;/code&gt; in your database and evaluate them at runtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computed fields&lt;/strong&gt; — derive a field's value from other fields using a JS expression&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content transformation&lt;/strong&gt; — map, filter, reshape API payloads or database rows with user-supplied logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflow / automation rules&lt;/strong&gt; — evaluate conditions and trigger actions defined by end users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature flags &amp;amp; A/B rules&lt;/strong&gt; — express targeting logic as scripts instead of hardcoded PHP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditional UI&lt;/strong&gt; — show/hide elements based on expressions like &lt;code&gt;status === "draft" &amp;amp;&amp;amp; role === "editor"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a standalone library with no framework dependency. &lt;code&gt;composer require aheinze/scriptlite&lt;/code&gt; and you're good.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some numbers
&lt;/h3&gt;

&lt;p&gt;Benchmarked on PHP 8.4 with 10 different workloads (fibonacci, quicksort, sieve, closures, tree traversal, matrix math, etc.):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Backend&lt;/th&gt;
&lt;th&gt;Total time&lt;/th&gt;
&lt;th&gt;vs PHP VM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PHP VM&lt;/td&gt;
&lt;td&gt;2608 ms&lt;/td&gt;
&lt;td&gt;1x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transpiler&lt;/td&gt;
&lt;td&gt;66 ms&lt;/td&gt;
&lt;td&gt;40x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C Extension&lt;/td&gt;
&lt;td&gt;14.6 ms&lt;/td&gt;
&lt;td&gt;178x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The transpiler gets within 3-4x of native PHP, which is honestly good enough for most use cases. The C extension is there for when you want to go full send.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require aheinze/scriptlite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the C extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pie &lt;span class="nb"&gt;install &lt;/span&gt;aheinze/scriptlite-ext
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repo: &lt;a href="https://github.com/aheinze/ScriptLite" rel="noopener noreferrer"&gt;https://github.com/aheinze/ScriptLite&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;909 tests across all backends. MIT licensed. PHP 8.3+.&lt;/p&gt;

&lt;p&gt;Would love to hear what you think, especially if you've run into similar "I need users to write logic but not PHP" situations. What did you end up doing?&lt;/p&gt;

</description>
      <category>php</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
