<?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: Benyamin Khalife</title>
    <description>The latest articles on DEV Community by Benyamin Khalife (@benkhalife).</description>
    <link>https://dev.to/benkhalife</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%2F3141137%2Fc20e0dbe-bc02-4da9-b419-2d7e81716203.jpg</url>
      <title>DEV Community: Benyamin Khalife</title>
      <link>https://dev.to/benkhalife</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/benkhalife"/>
    <language>en</language>
    <item>
      <title>Why You Should Stop Using Laravel for Commercial Projects (The Hidden Risks)</title>
      <dc:creator>Benyamin Khalife</dc:creator>
      <pubDate>Tue, 26 May 2026 13:57:50 +0000</pubDate>
      <link>https://dev.to/benkhalife/why-you-should-stop-using-laravel-for-commercial-projects-the-hidden-risks-3147</link>
      <guid>https://dev.to/benkhalife/why-you-should-stop-using-laravel-for-commercial-projects-the-hidden-risks-3147</guid>
      <description>&lt;p&gt;Every framework has its honeymoon phase. For PHP developers, Laravel has been that reliable, feature-rich partner for years. It’s elegant, it has an amazing community, and it gets things done fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But fast development doesn't always mean a sustainable commercial product.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the recent wave of supply chain attacks targeting popular Laravel ecosystem dependencies (like &lt;code&gt;localization/lang&lt;/code&gt; packages and ignition RCEs), it’s time to take off the rose-colored glasses. &lt;em&gt;Here is why choosing Laravel&lt;/em&gt; for your next enterprise or high-scale commercial project might be a massive technical debt in disguise.&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%2Fe8uzhw8hp2bqba7zjoa0.jpg" 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%2Fe8uzhw8hp2bqba7zjoa0.jpg" alt="The " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The "Bloatware" Dilemma: Heavy, Slow, and Resource-Hungry
&lt;/h2&gt;

&lt;p&gt;Laravel is undeniably bloated. Out of the box, it boots up a massive container, hundreds of classes, and an abstraction layer for almost everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Cost:&lt;/strong&gt; While this makes writing code easy, it consumes significant memory and CPU cycles per request compared to modern micro-frameworks or compiled languages like Go and Rust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commercial Impact:&lt;/strong&gt; In a production environment, higher resource consumption directly translates to bloated cloud infrastructure bills. When scaling to millions of requests, Laravel requires expensive horizontal scaling setups far sooner than it should.&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%2Fwal2x5b0d4xp3ygivjag.jpg" 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%2Fwal2x5b0d4xp3ygivjag.jpg" alt="The Dependency Hell" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Dependency Hell &amp;amp; Supply Chain Vulnerabilities
&lt;/h2&gt;

&lt;p&gt;Laravel doesn't live in a vacuum; it heavily relies on Symfony components and a massive tree of third-party packages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Risk:&lt;/strong&gt; You aren't just trusting Taylor Otwell; you are trusting hundreds of anonymous open-source maintainers. As we recently witnessed, hackers are actively exploiting this by poisoning the ecosystem (e.g., embedding backdoors in language and helper packages).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Control Problem:&lt;/strong&gt; Managing, auditing, and securing a giant vendor folder in Laravel is a logistical nightmare. One compromised sub-dependency can instantly introduce a Remote Code Execution (RCE) vulnerability into your commercial app.&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%2Fnn7ymolu37phiy2k4nuy.webp" 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%2Fnn7ymolu37phiy2k4nuy.webp" alt="The " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The "Mass Popularity" Target
&lt;/h2&gt;

&lt;p&gt;Being the king of PHP frameworks comes with a massive bullseye on your back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Target:&lt;/strong&gt; Hackers love popular frameworks. Why waste time finding a zero-day vulnerability for a custom or niche framework when finding one exploit in Laravel or its core dependencies grants you access to hundreds of thousands of live websites?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated Exploitation:&lt;/strong&gt; Shodan and automated botnets constantly scan the web for Laravel specific patterns (like exposed .env files or older Ignition pages). The moment an exploit is published, your site is targeted within minutes.&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%2Fkv975onf8wib859vu7f5.jpg" 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%2Fkv975onf8wib859vu7f5.jpg" alt="Architectural Magic" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Architectural Magic That Breaks at Scale
&lt;/h2&gt;

&lt;p&gt;Laravel relies heavily on "magic" methods, Facades, and Eloquent ORM. While great for rapid prototyping, this magic obfuscates what is actually happening under the hood.&lt;/p&gt;

&lt;p&gt;As your data grows, Eloquent can easily introduce N+1 query problems and hidden memory leaks if not meticulously optimized.&lt;/p&gt;

&lt;p&gt;Deeply nesting your business logic inside Laravel's heavily opinionated structure makes migrating or rewriting microservices incredibly difficult later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion: Is Laravel Dead?
&lt;/h3&gt;

&lt;p&gt;No. It’s still fantastic for MVPs, SaaS internal tools, or small-to-medium business apps.&lt;/p&gt;

&lt;p&gt;However, if you are building an enterprise-grade, high-performance, and security-critical commercial system, the heavy architectural footprint and the massive attack surface of its ecosystem should make you think twice. Sometimes, going lighter, stricter, and more compiled is the only way to scale safely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s your take?&lt;/strong&gt; Have you migrated away from Laravel for performance or security reasons? Let's discuss below!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>cybersecurity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop Scanners from Hammering Your PHP App — Without a Database or External Services</title>
      <dc:creator>Benyamin Khalife</dc:creator>
      <pubDate>Sat, 16 May 2026 12:46:35 +0000</pubDate>
      <link>https://dev.to/benkhalife/stop-scanners-from-hammering-your-php-app-without-a-database-or-external-services-39og</link>
      <guid>https://dev.to/benkhalife/stop-scanners-from-hammering-your-php-app-without-a-database-or-external-services-39og</guid>
      <description>&lt;p&gt;Every day, automated bots are scanning your website. Not just yours — everyone's. They probe for exposed &lt;code&gt;.env&lt;/code&gt; files, old WordPress admin panels, SQL injection points, and known CVEs. Some of them send thousands of requests per minute, not because they're targeting &lt;em&gt;you&lt;/em&gt; specifically, but because scanning the entire internet is cheap and easy.&lt;/p&gt;

&lt;p&gt;This article is about what's actually happening out there, why it matters even for small projects, and how a lightweight PHP library can help you deal with it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Are These Scanners, Exactly?
&lt;/h2&gt;

&lt;p&gt;Web scanners are automated programs that crawl the internet looking for security vulnerabilities. Some are legitimate security tools — like Shodan or Censys — that map the internet for research purposes. But many others are operated by attackers who are looking for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exposed configuration files&lt;/strong&gt; — &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;web.config&lt;/code&gt;, &lt;code&gt;.git/config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unprotected admin panels&lt;/strong&gt; — &lt;code&gt;/wp-admin&lt;/code&gt;, &lt;code&gt;/phpmyadmin&lt;/code&gt;, &lt;code&gt;/adminer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Known vulnerable endpoints&lt;/strong&gt; — like &lt;code&gt;xmlrpc.php&lt;/code&gt; in WordPress or &lt;code&gt;setup.php&lt;/code&gt; left behind after an install&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL injection and XSS vectors&lt;/strong&gt; — via query strings, POST bodies, and cookies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path traversal attacks&lt;/strong&gt; — trying &lt;code&gt;../../etc/passwd&lt;/code&gt; variations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tools they use are well-known: &lt;code&gt;sqlmap&lt;/code&gt;, &lt;code&gt;nikto&lt;/code&gt;, &lt;code&gt;nmap&lt;/code&gt;, &lt;code&gt;dirbuster&lt;/code&gt;, &lt;code&gt;gobuster&lt;/code&gt;, &lt;code&gt;masscan&lt;/code&gt;, and many more. These aren't obscure hacking tools — they're freely available, widely documented, and actively maintained.&lt;/p&gt;

&lt;p&gt;Even if your app has no actual vulnerabilities, these scanners generate noise, consume server resources, pollute your logs, and can slow down real users during a burst of requests.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Server-Level Solutions Aren't Always Enough
&lt;/h2&gt;

&lt;p&gt;You might wonder: "Can't Nginx or Apache handle this?"&lt;/p&gt;

&lt;p&gt;Sort of. You can write firewall rules, block known bad IPs, or configure rate limiting in your server config. But these approaches have limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They require system-level access (not always available on shared hosting)&lt;/li&gt;
&lt;li&gt;They need manual maintenance as attack patterns evolve&lt;/li&gt;
&lt;li&gt;They can't easily inspect request payloads for things like SQL injection signatures&lt;/li&gt;
&lt;li&gt;They don't integrate naturally with your application logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A PHP-level firewall runs inside your application, which means it can inspect everything: headers, query strings, POST bodies, cookies, request paths, and user agents — and react accordingly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing xZeroProtect
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/webrium/xzeroprotect" rel="noopener noreferrer"&gt;xZeroProtect&lt;/a&gt; is a lightweight, file-based PHP 8 firewall with zero external dependencies. No Redis, no MySQL, no external services — everything is stored on disk.&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%2Fkxl1kmwc7d02htmfxa47.webp" 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%2Fkxl1kmwc7d02htmfxa47.webp" alt="Introducing xZeroProtect layouts" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install it via Composer:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The simplest possible integration — two lines at the top of your &lt;code&gt;index.php&lt;/code&gt;:&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="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Webrium\XZeroProtect\XZeroProtect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;XZeroProtect&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Default rules activate immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  What It Actually Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Blocks Known Scanner User Agents
&lt;/h3&gt;

&lt;p&gt;The library maintains a list of User-Agent signatures associated with scanning tools and attack frameworks: &lt;code&gt;sqlmap&lt;/code&gt;, &lt;code&gt;nikto&lt;/code&gt;, &lt;code&gt;nessus&lt;/code&gt;, &lt;code&gt;acunetix&lt;/code&gt;, &lt;code&gt;masscan&lt;/code&gt;, &lt;code&gt;dirbuster&lt;/code&gt;, &lt;code&gt;gobuster&lt;/code&gt;, &lt;code&gt;feroxbuster&lt;/code&gt;, &lt;code&gt;wfuzz&lt;/code&gt;, &lt;code&gt;ffuf&lt;/code&gt;, &lt;code&gt;hydra&lt;/code&gt;, &lt;code&gt;metasploit&lt;/code&gt;, and many others. Requests from these tools are blocked before they get anywhere near your application logic.&lt;/p&gt;

&lt;p&gt;You can extend or trim this list:&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;$firewall&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'custom-bad-bot'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$firewall&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;removeAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'curl'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// if your API clients use curl legitimately&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Blocks Sensitive Path Probes
&lt;/h3&gt;

&lt;p&gt;Scanners routinely probe paths that don't exist in modern PHP apps — and shouldn't. xZeroProtect blocks requests targeting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CMS admin panels: &lt;code&gt;/wp-admin&lt;/code&gt;, &lt;code&gt;/wp-login&lt;/code&gt;, &lt;code&gt;/administrator&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Config files: &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;.git&lt;/code&gt;, &lt;code&gt;.htpasswd&lt;/code&gt;, &lt;code&gt;web.config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Database tools: &lt;code&gt;/phpmyadmin&lt;/code&gt;, &lt;code&gt;/adminer&lt;/code&gt;, &lt;code&gt;/dbadmin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dangerous files: &lt;code&gt;.sql&lt;/code&gt;, &lt;code&gt;.bak&lt;/code&gt;, &lt;code&gt;.backup&lt;/code&gt;, &lt;code&gt;dump.sql&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Web shells: &lt;code&gt;shell.php&lt;/code&gt;, &lt;code&gt;c99.php&lt;/code&gt;, &lt;code&gt;r57.php&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Path traversal: &lt;code&gt;../&lt;/code&gt;, &lt;code&gt;..%2f&lt;/code&gt;, &lt;code&gt;%2e%2e&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're running a fully-routed PHP app with no &lt;code&gt;.php&lt;/code&gt; files in URLs, you can add that as a rule too:&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;$firewall&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.php'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Scans Payloads for Attack Signatures
&lt;/h3&gt;

&lt;p&gt;The payload scanner inspects GET parameters, POST body, raw input, and cookies using compiled regular expressions. It detects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL injection variants: &lt;code&gt;UNION SELECT&lt;/code&gt;, &lt;code&gt;DROP TABLE&lt;/code&gt;, &lt;code&gt;SLEEP()&lt;/code&gt;, &lt;code&gt;BENCHMARK()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;XSS: &lt;code&gt;&amp;lt;script&lt;/code&gt; tags, inline event handlers, &lt;code&gt;javascript:&lt;/code&gt; protocol&lt;/li&gt;
&lt;li&gt;Path traversal: &lt;code&gt;../&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;PHP code injection: &lt;code&gt;system()&lt;/code&gt;, &lt;code&gt;exec()&lt;/code&gt;, &lt;code&gt;eval(base64_decode(...))&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;LFI: &lt;code&gt;/etc/passwd&lt;/code&gt;, &lt;code&gt;/etc/shadow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Remote file inclusion&lt;/li&gt;
&lt;li&gt;Command injection via shell metacharacters&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Rate Limiting Without Redis
&lt;/h3&gt;

&lt;p&gt;The rate limiter uses a sliding-window counter stored per-IP on disk. No Redis or Memcached required.&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;$firewall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;XZeroProtect&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'rate_limit'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'max_requests'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'per_seconds'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&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;p&gt;When a client exceeds the limit, they accumulate violations. After enough violations, they get temporarily banned. After enough bans, permanently.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Doesn't Block Legitimate Crawlers
&lt;/h3&gt;

&lt;p&gt;This is important: Google, Bing, and other legitimate search engines should not get caught in your firewall. xZeroProtect handles this with &lt;strong&gt;double-DNS verification&lt;/strong&gt; for major crawlers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Resolve the visitor's IP to a hostname via reverse DNS&lt;/li&gt;
&lt;li&gt;Confirm the hostname ends with the expected suffix (e.g. &lt;code&gt;.googlebot.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Re-resolve that hostname back to an IP&lt;/li&gt;
&lt;li&gt;Confirm the re-resolved IP matches the original&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Anyone can fake a User-Agent string. DNS cannot be faked. This is the same verification method recommended by Google and Bing in their official documentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Learning Mode: Test Before You Block
&lt;/h2&gt;

&lt;p&gt;Before switching to production, you can run in learning mode. All threats are detected and logged, but nothing is blocked. This lets you review what would have been caught and tune your configuration accordingly.&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;// During tuning&lt;/span&gt;
&lt;span class="nc"&gt;XZeroProtect&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'mode'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'learning'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Once satisfied&lt;/span&gt;
&lt;span class="nc"&gt;XZeroProtect&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'mode'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'production'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is genuinely useful. Running learning mode for a few days on a real server will show you the volume and variety of automated probes hitting your application — and help you avoid accidentally blocking legitimate traffic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Apache Integration for Zero-PHP-Overhead Blocking
&lt;/h2&gt;

&lt;p&gt;For permanently banned IPs, you can optionally write them into &lt;code&gt;.htaccess&lt;/code&gt; so Apache rejects the connection before PHP even starts:&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;$firewall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;XZeroProtect&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'apache_blocking'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'htaccess_path'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/.htaccess'&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;p&gt;This is particularly useful for confirmed attackers who you never want to reach your application layer again.&lt;/p&gt;




&lt;h2&gt;
  
  
  Custom Rules
&lt;/h2&gt;

&lt;p&gt;If the built-in modules don't cover a specific case, you can register your own:&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="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Webrium\XZeroProtect\RuleResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$firewall&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'no-php-extension'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str_ends_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'.php'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RuleResult&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PHP extension not valid on this server'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RuleResult&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;pass&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;p&gt;Rules can block, log without blocking, or pass. They run in priority order and can be enabled, disabled, or removed at runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  What xZeroProtect Is Not
&lt;/h2&gt;

&lt;p&gt;To be clear about expectations: this is not a commercial WAF. It won't stop a sophisticated, targeted attack from a determined adversary. It doesn't do deep packet inspection, it doesn't have a threat intelligence feed, and it doesn't update its rule set automatically.&lt;/p&gt;

&lt;p&gt;What it does is significantly reduce the automated noise that hits most PHP applications — the opportunistic scanners, the script-kiddie tools, the mass-probing bots — with a setup that takes minutes and has no infrastructure dependencies.&lt;/p&gt;

&lt;p&gt;For many projects, especially those on shared hosting or small VPS instances where you don't have the option to configure Nginx rate limiting or cloud WAF rules, this is a practical and useful layer of defense.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



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

&lt;/div&gt;



&lt;p&gt;Full documentation and source: &lt;a href="https://github.com/webrium/xzeroprotect" rel="noopener noreferrer"&gt;github.com/webrium/xzeroprotect&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start in learning mode, review your logs, tune your configuration, then flip to production.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have feedback or found an edge case? Issues and PRs are welcome on GitHub.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>php</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Building a Modular Ecosystem: How I’m Rethinking the CLI for My Personal</title>
      <dc:creator>Benyamin Khalife</dc:creator>
      <pubDate>Fri, 15 May 2026 18:01:04 +0000</pubDate>
      <link>https://dev.to/benkhalife/building-a-modular-ecosystem-how-im-rethinking-the-cli-for-my-personal-4p95</link>
      <guid>https://dev.to/benkhalife/building-a-modular-ecosystem-how-im-rethinking-the-cli-for-my-personal-4p95</guid>
      <description>&lt;p&gt;Every developer has that one project they pour their soul into. For me, it’s &lt;strong&gt;Webrium&lt;/strong&gt; — my personal PHP framework. Recently, I’ve been focusing on its "brain" for command-line operations: the &lt;strong&gt;Webrium Console&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can check out the progress here: &lt;a href="https://github.com/webrium/console" rel="noopener noreferrer"&gt;https://github.com/webrium/console&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vision: Beyond Simple Commands
&lt;/h2&gt;

&lt;p&gt;While I’ve always admired how Laravel’s Artisan simplifies development, I wanted to build something that fits a specific architectural need I had: &lt;strong&gt;True Modularity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal for the Webrium Console wasn't just to run migrations or seeders. I wanted a system where you can build a website normally, and then "export" or "modularize" specific parts of it as independent plugins or components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning Features into Components
&lt;/h2&gt;

&lt;p&gt;The most exciting part of this console library is how it handles site sections. Imagine you’ve built a robust &lt;strong&gt;Admin Panel&lt;/strong&gt; or a &lt;strong&gt;Blog system&lt;/strong&gt;. Instead of having them buried deep within your project’s monolith, Webrium Console allows you to treat them as portable components.&lt;br&gt;
For example:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Blog Component: It’s not just a set of files. It includes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;everything—the management interface for the admin and the public-facing views for the client.&lt;/li&gt;
&lt;li&gt;The Admin Portal: You can build it once and potentially plug it into different projects managed by the framework.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This "extract and reuse" approach is what I’m currently refining. It’s about being able to say: "I need a blog on this new site," and simply plugging in your pre-built, console-managed component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I’d love to hear your thoughts on this:&lt;/strong&gt;&lt;br&gt;
Laravel is the industry standard for a reason, but sometimes we seek something different. What is the one thing that would make you look beyond established frameworks like Laravel and try something newer or custom-built for your projects?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do you need a custom website or a specialized web solution?&lt;br&gt;
Feel free to reach out! I’m available for new projects, or even just a free consultation to help you figure out the best tech stack for your needs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s connect:&lt;br&gt;
Telegram: @benyaminir&lt;br&gt;
Email: &lt;a href="mailto:benkhalifedev@gmail.com"&gt;benkhalifedev@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>webdev</category>
      <category>framework</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Generating Sitemaps is Easy</title>
      <dc:creator>Benyamin Khalife</dc:creator>
      <pubDate>Thu, 14 May 2026 06:02:44 +0000</pubDate>
      <link>https://dev.to/benkhalife/generating-sitemaps-is-easy-pa0</link>
      <guid>https://dev.to/benkhalife/generating-sitemaps-is-easy-pa0</guid>
      <description>&lt;p&gt;I've been building PHP projects for a while, and one of those annoying little tasks that keeps coming up is generating sitemaps. Not because it's technically hard — it's just tedious. You either roll your own XML string builder (and inevitably forget an edge case), or you pull in a heavy package that does ten things you don't need.&lt;br&gt;
So I wrote a small library called webrium/sitemap to handle it cleanly. No bloat, just a focused tool that covers the real-world cases: images, videos, hreflang for multilingual sites, gzip, and automatic splitting for large sites.&lt;/p&gt;


&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;


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

&lt;/div&gt;


&lt;p&gt;That's it. PHP 8.1+ and the xmlwriter extension (which is almost always enabled by default).&lt;/p&gt;


&lt;h2&gt;
  
  
  The Basics
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Webrium\Sitemap\Sitemap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$sitemap&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;Sitemap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="n"&gt;changefreq&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Sitemap&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FREQ_DAILY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// → https://example.com&lt;/span&gt;
&lt;span class="nv"&gt;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/about'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;changefreq&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Sitemap&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FREQ_MONTHLY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/blog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;changefreq&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Sitemap&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FREQ_DAILY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;One thing worth noting: passing &lt;code&gt;'/'&lt;/code&gt; for the homepage correctly produces &lt;code&gt;https://example.com&lt;/code&gt; — no trailing slash appended. Clean URLs matter, especially when Google is reading them.&lt;/p&gt;


&lt;h2&gt;
  
  
  Full URL Options
&lt;/h2&gt;

&lt;p&gt;Each URL can carry a lastmod, changefreq, and priority. Named arguments make this readable even when you're using all of them:&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="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="s1"&gt;'/blog/my-first-post'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lastmod&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;DateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2025-04-15'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;changefreq&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Sitemap&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FREQ_WEEKLY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="mf"&gt;0.7&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;changefreq tells crawlers how often the content changes. Available values:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;changefreq&lt;/strong&gt; tells crawlers how often the content changes. Available values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constant&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Sitemap::FREQ_ALWAYS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;always&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Sitemap::FREQ_HOURLY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;hourly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Sitemap::FREQ_DAILY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;daily&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Sitemap::FREQ_WEEKLY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;weekly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Sitemap::FREQ_MONTHLY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;monthly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Sitemap::FREQ_YEARLY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;yearly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Sitemap::FREQ_NEVER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;never&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;priority&lt;/strong&gt; is a float between &lt;code&gt;0.0&lt;/code&gt; and &lt;code&gt;1.0&lt;/code&gt;. Your homepage is typically &lt;code&gt;1.0&lt;/code&gt;, blog posts somewhere around &lt;code&gt;0.6&lt;/code&gt;–&lt;code&gt;0.8&lt;/code&gt;. Search engines don't treat this as a hard rule, but it's still worth setting thoughtfully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;lastmod&lt;/strong&gt; accepts any &lt;code&gt;DateTimeInterface&lt;/code&gt;, so you can pass a &lt;code&gt;DateTime&lt;/code&gt;, &lt;code&gt;DateTimeImmutable&lt;/code&gt;, or Carbon instance from your models directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding URLs in Bulk
&lt;/h2&gt;

&lt;p&gt;If you're pulling pages from a database, &lt;code&gt;addUrls()&lt;/code&gt; is cleaner than looping manually:&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;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addUrls&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/services'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'priority'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/portfolio'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'changefreq'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Sitemap&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FREQ_MONTHLY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/contact'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="s1"&gt;'lastmod'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2025-01-10'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'priority'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&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;p&gt;Duplicate URLs are silently ignored — you don't need to deduplicate before passing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Images
&lt;/h2&gt;

&lt;p&gt;Google's image sitemap extension lets you associate images with pages, which can improve image search visibility:&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;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s1"&gt;'/portfolio/project-alpha'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&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;'loc'&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/images/project-alpha-hero.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'title'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Project Alpha — hero shot'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'caption'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'The main dashboard view of Project Alpha.'&lt;/span&gt;&lt;span class="p"&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;'loc'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/images/project-alpha-mobile.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Project Alpha — mobile view'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&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;p&gt;&lt;code&gt;loc&lt;/code&gt; is required. &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;caption&lt;/code&gt; are optional but recommended.&lt;/p&gt;




&lt;h2&gt;
  
  
  Videos
&lt;/h2&gt;

&lt;p&gt;Same idea for videos, with a few more required fields:&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;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s1"&gt;'/tutorials/getting-started'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;videos&lt;/span&gt;&lt;span class="o"&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;'thumbnail_loc'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/thumbs/getting-started.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'title'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Getting Started in 5 Minutes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'description'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'A quick walkthrough of the core features.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'duration'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// seconds&lt;/span&gt;
        &lt;span class="p"&gt;],&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;p&gt;&lt;code&gt;thumbnail_loc&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, and &lt;code&gt;description&lt;/code&gt; are required. &lt;code&gt;duration&lt;/code&gt; is optional.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multilingual Sites (Hreflang)
&lt;/h2&gt;

&lt;p&gt;If your site serves content in multiple languages, you can attach hreflang alternates to each URL:&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;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s1"&gt;'/about'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;hreflangs&lt;/span&gt;&lt;span class="o"&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;'lang'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/en/about'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'lang'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'fa'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/fa/about'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'lang'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'x-default'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/about'&lt;/span&gt;&lt;span class="p"&gt;],&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;p&gt;The &lt;code&gt;x-default&lt;/code&gt; entry tells Google which version to show when no language preference matches.&lt;/p&gt;




&lt;h2&gt;
  
  
  Saving to File
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Plain XML&lt;/span&gt;
&lt;span class="nv"&gt;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;saveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/var/www/public/sitemap.xml'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Gzip compressed — just add .gz to the filename&lt;/span&gt;
&lt;span class="nv"&gt;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;saveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/var/www/public/sitemap.xml.gz'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gzip is worth enabling in production. The file ends up 5–10x smaller, and most crawlers handle it fine.&lt;/p&gt;




&lt;h2&gt;
  
  
  Large Sites — Auto Splitting
&lt;/h2&gt;

&lt;p&gt;The sitemap protocol caps files at 50,000 URLs and 50 MB. For bigger sites, &lt;code&gt;splitAndSave()&lt;/code&gt; handles the splitting automatically and generates a sitemap index:&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;$sitemap&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;Sitemap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$allPages&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$indexXml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$sitemap&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;splitAndSave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s1"&gt;'/var/www/public/sitemaps'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;baseFileUrl&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/sitemaps'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s1"&gt;'sitemap'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gzip&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;file_put_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/var/www/public/sitemap-index.xml'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$indexXml&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces &lt;code&gt;sitemap-1.xml.gz&lt;/code&gt;, &lt;code&gt;sitemap-2.xml.gz&lt;/code&gt;, and so on — plus a sitemap index file that points to all of them. Submit the index URL to Google Search Console and you're done.&lt;/p&gt;




&lt;h2&gt;
  
  
  Namespace Cleanliness
&lt;/h2&gt;

&lt;p&gt;One small detail I care about: the library only adds XML namespaces that are actually used. If your sitemap has no images or videos, the output won't carry &lt;code&gt;xmlns:image&lt;/code&gt; or &lt;code&gt;xmlns:video&lt;/code&gt;. It's a minor thing, but it keeps the XML clean and honest.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Nothing revolutionary here — it's a sitemap generator. But it handles the real cases correctly and gets out of your way. If you've been copy-pasting sitemap XML by hand or using a bloated package, give it a try.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/webrium/sitemap" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; / Packagist:&lt;/strong&gt; &lt;code&gt;composer require webrium/sitemap&lt;/code&gt;&lt;/p&gt;




</description>
      <category>php</category>
      <category>seo</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>SQL And PHP: People whose birthday is today</title>
      <dc:creator>Benyamin Khalife</dc:creator>
      <pubDate>Fri, 23 May 2025 12:58:06 +0000</pubDate>
      <link>https://dev.to/benkhalife/sql-and-php-people-whose-birthday-is-today-157j</link>
      <guid>https://dev.to/benkhalife/sql-and-php-people-whose-birthday-is-today-157j</guid>
      <description>&lt;h2&gt;
  
  
  How do we find a list of people whose birthday is today from a database and perform an operation on them?
&lt;/h2&gt;

&lt;p&gt;To start, we add the &lt;a href="https://github.com/webrium/foxdb" rel="noopener noreferrer"&gt;Foxdb&lt;/a&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require webrium&lt;span class="se"&gt;\f&lt;/span&gt;oxdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this way, write the default configuration for using the library and change it according to your project.&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="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Foxdb\DB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Foxdb\Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Foxdb\DB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;addConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'host'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'port'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'3306'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="s1"&gt;'database'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'1234'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="s1"&gt;'charset'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'collation'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UTF8_GENERAL_CI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'fetch'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FETCH_CLASS&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, using a simple query, we can get the list of people whose birthday is today.&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;$list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;month&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'birth_date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'m'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;day&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'birth_date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'d'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  
          &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the GitHub repository and documentation at the link below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/webrium/foxdb" rel="noopener noreferrer"&gt;FoxDB Documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>oop</category>
      <category>database</category>
      <category>mysql</category>
      <category>mariadb</category>
    </item>
    <item>
      <title>Download photo from Telegram Bot !</title>
      <dc:creator>Benyamin Khalife</dc:creator>
      <pubDate>Fri, 23 May 2025 11:11:11 +0000</pubDate>
      <link>https://dev.to/benkhalife/download-photo-from-telegram-bot--1d1n</link>
      <guid>https://dev.to/benkhalife/download-photo-from-telegram-bot--1d1n</guid>
      <description>&lt;p&gt;In the last Changes in &lt;code&gt;2.0.0.beta2&lt;/code&gt; version, we can download photos sent in the Bot from Telegram servers  and save them on our own server&lt;/p&gt;

&lt;h4&gt;
  
  
  Install the Botfire library using Composer:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require botfire/botfire:2.0.0.beta2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To download a photo:&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;// Where to store photos on the server&lt;/span&gt;
&lt;span class="nv"&gt;$dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'/photos/'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By executing the &lt;code&gt;download&lt;/code&gt; method as in the example above, the file is saved in the path we specified in $dir&lt;/p&gt;

&lt;p&gt;With the forEach method, we can iterate through all the images of different sizes.&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="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Photo&lt;/span&gt; &lt;span class="nv"&gt;$photo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$photo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getWidth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'_@name'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$photo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$name&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;p&gt;In the line &lt;code&gt;$name = $photo-&amp;gt;getWidth().'_@name';&lt;/code&gt; we customize the filename. We add the width value to the beginning of the filename.&lt;br&gt;
The &lt;code&gt;@name&lt;/code&gt; expression in the &lt;code&gt;download&lt;/code&gt; function is replaced with the original file name, giving us more flexibility in renaming the image.&lt;/p&gt;

&lt;p&gt;I think the same structure would be suitable for other file types as well. So audio, video, and other types should be implemented and appropriate comments and documentation should be created for them.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Send and receive photo from the Telegram bot</title>
      <dc:creator>Benyamin Khalife</dc:creator>
      <pubDate>Mon, 19 May 2025 16:07:39 +0000</pubDate>
      <link>https://dev.to/benkhalife/send-and-receive-files-from-the-telegram-bot-3h54</link>
      <guid>https://dev.to/benkhalife/send-and-receive-files-from-the-telegram-bot-3h54</guid>
      <description>&lt;p&gt;The next important feature that needed to be implemented was the ability to send photos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending photos in a Telegram bot with &lt;a href="https://github.com/botfire/botfire" rel="noopener noreferrer"&gt;BotFire&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;There are three ways to send a photo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Send via link
&lt;/h3&gt;

&lt;p&gt;For example, we have a picture that can be downloaded from the address &lt;code&gt;https://download.samplelib.com/png/sample-bumblebee-400x300.png&lt;/code&gt;. Using the link, the file can be sent to the bot.&lt;br&gt;
Example:&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;$link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'https://download.samplelib.com/png/sample-bumblebee-400x300.png'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Send photo contents
&lt;/h3&gt;

&lt;p&gt;In this method, our file does not need to be accessible via a link. Our file exists somewhere on the server and can be sent using &lt;code&gt;Bot::inputFile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example:&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;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;inputFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__DIR__&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'/assets/test.png'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to add a caption to a photo?&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="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello caption 😃'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Receive a photo from the client in the bot
&lt;/h2&gt;

&lt;p&gt;To find out whether the type of message the client sent to the bot is a photo or not, we can do the following:&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="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TYPE_PHOTO&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
 &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the client has sent a photo, we can access its information through the &lt;code&gt;photo&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Telegram sends us a &lt;code&gt;file_id&lt;/code&gt; which contains a unuque identifier and if you save this identifier you can send the photo later without having to upload it again (&lt;a href="https://core.telegram.org/bots/api#sending-files" rel="noopener noreferrer"&gt;more information&lt;/a&gt;)&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="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TYPE_PHOTO&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;

  &lt;span class="nv"&gt;$file_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fileId&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;p&gt;Telegram sends us an array of images of different sizes, the first one being the lowest and the last one being the highest resolution and size.&lt;br&gt;
In the example above, we received the first one. Other methods are also available.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;first&lt;/td&gt;
&lt;td&gt;Returns the first value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last&lt;/td&gt;
&lt;td&gt;Reterns the last value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;count&lt;/td&gt;
&lt;td&gt;Number of arrays&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;indexOf&lt;/td&gt;
&lt;td&gt;Returns the value based on its index.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;asArray&lt;/td&gt;
&lt;td&gt;Returns the entire array in its entirety.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If the photo has a caption, you can get it like this:&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;$caption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there is no caption, it returns &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, if you have access to the file_id, you can resend that image in the same way as explained:&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;$file_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$file_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'New Caption ...'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>php</category>
      <category>telegram</category>
      <category>development</category>
      <category>botfire</category>
    </item>
    <item>
      <title>Managing CallbackQuery and message types in Telegram bots</title>
      <dc:creator>Benyamin Khalife</dc:creator>
      <pubDate>Sat, 17 May 2025 18:29:37 +0000</pubDate>
      <link>https://dev.to/benkhalife/managing-callbackquery-and-message-types-in-telegram-bots-14c3</link>
      <guid>https://dev.to/benkhalife/managing-callbackquery-and-message-types-in-telegram-bots-14c3</guid>
      <description>&lt;p&gt;Continuing from the &lt;a href="https://dev.to/benkhalife/telegram-bot-librar-development-with-php-22hn"&gt;previous article&lt;/a&gt;, this time I will describe the changes I made to the library.&lt;/p&gt;

&lt;p&gt;The user in the Telegram bot can send different types of messages, such as &lt;strong&gt;text messages&lt;/strong&gt;, &lt;strong&gt;photos&lt;/strong&gt;, &lt;strong&gt;videos&lt;/strong&gt;, etc. In some cases, we need to know what type of message the client has sent so that we can respond to it accordingly, or perhaps we need to limit the type of message for the client&lt;/p&gt;

&lt;p&gt;So, a method had to be created to detect the type of message sent by the client, and as a result, the message type could be received using the following method:&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;$type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I think this is a clean and simple syntax, don't you?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The values ​​it returns are one of these:&lt;br&gt;
'text', 'photo', 'video', 'audio', 'document', 'sticker', 'animation', 'location', 'contact', 'poll'&lt;/p&gt;

&lt;p&gt;I also put the consts of these types in the message method, which are:&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="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_TEXT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_PHOTO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'photo'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_VIDEO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'video'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_AUDIO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'audio'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_DOCUMENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'document'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_STICKER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'sticker'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_ANIMATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'animation'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_LOCATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'location'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_CONTACT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'contact'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_POLL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'poll'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_MESSAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These constants are accessible from the Message class, so creating a condition to check the type of user message could look something like this:&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="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TYPE_TEXT&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="c1"&gt;// Do something.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Please send your message as text.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&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;blockquote&gt;
&lt;p&gt;What is your opinion about this structure? I would be happy if you share it with me.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Detecting a query callback in a Telegram bot
&lt;/h2&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%2Fq4z4bi5punm9c7wjd9ok.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%2Fq4z4bi5punm9c7wjd9ok.png" alt=" " width="449" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is another type of message. When the client clicks on an inline button In this case, Telegram sends us a Callback that also contains the data object of that message.&lt;/p&gt;

&lt;p&gt;I created the isCallbackQuery method for this purpose, which can be used as follows:&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;isCallbackQuery&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;Bot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"🛜 Get Callback Query:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Data : &lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&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;p&gt;So if the value of &lt;code&gt;Bot::isCallbackQuery()&lt;/code&gt; is true, we know that the client has clicked a button. In this case, we have access to the callback method and we can get the data of the button that the client clicked using &lt;code&gt;Bot::callback()-&amp;gt;data()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;View on GitHub: &lt;a href="https://github.com/botfire/botfire" rel="noopener noreferrer"&gt;BotFire&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This library is under development and is not yet ready for use.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>telegram</category>
      <category>library</category>
      <category>development</category>
      <category>bot</category>
    </item>
  </channel>
</rss>
