慣性聚合 高效追讀感興趣之博客、新聞、科技資訊
閱原文 以慣性聚合開啟

推薦訂閱源

博客园 - 司徒正美
V
V2EX
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
月光博客
月光博客
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
何故`mixed`乃汝PHP代码库之最劣类型(及其灭绝之道)
Gabriel Anha · 2026-05-24 · via DEV Community

上月与吾言谈之团队,有千四百人mixed 返类型于其 Laravel 11 代码之林。PHPStan 级别六为青。测试无碍。一初学者重构一助函数,三控制器于测试环境返 HTTP 500。无物示警。无物可察。

mixed 乃 PHP 类型系统言“吾已竭力。” Laravel 自身之文载此意于四处。PHPStan 容之于级别七。汝之代码林默然积之。每mixed乃隐于下流之断裂,非目可见,直至其时始现。

五类混凝土之崩坏,继以五十行脚本,寻汝之最凶者。

mixed之真意何在(及何故"anything"为患)

mixed乃PHP 8.0所增,为"无类型"之显式版本。其纳intstringarrayobjectnullfalse,閉包,資源。凡PHP所能容者,皆在焉。此乃語言之至型也.

然有陷阱:至型無以示人。若函數歸mixed,則呼者必須為每支每較每法皆懷惡境。編譯器(PHP之例,則為靜態分析器)無可依據。

八点前之变,乃失其返类型,靜工器视之如一。八点無新患,唯舊患賦名。名無所助,反使記述易而標識難。

失一:IDE自动补全功能每用必死mixed归也

汝书之$order = $repo->find($id);且击之->,PHPStorm于汝无益。甚者,乃予汝项目诸类之法,悉以字母序列。汝卷过__constructacceptacknowledgeactAs……

// Repository.php: the kind of code that ages badly
final class OrderRepository
{
    public function find(int $id): mixed  // <-- the crime
    {
        $row = DB::table('orders')->find($id);
        if (!$row) {
            return null;
        }
        return Order::fromRow($row);
    }
}

全屏模式 退出全屏模式

其签署曰"可任一物"。故 PHPStorm 以为然。汝失"定位于定义"之能。汝失于Order字段之重命名重构。汝失"汝于->customerId上调用null"之警示。汝于二零二六年而书 PHP 5.6.

其解乃机巧耳:

public function find(int $id): ?Order
{
    $row = DB::table('orders')->find($id);
    return $row ? Order::fromRow($row) : null;
}

入全景模式 出全景模式

一言而已。IDE苏醒,重命名重构遍及应有之处。后之开发者读之find()能知其返,不须检其文。

失二:PHPStan等级六以上,不能捕获空引用错误于mixed

此乃生产中噬人之败式。PHPStan可证诸君之码甚多,然mixed乃墙也。

public function totalForCustomer(int $customerId): float
{
    $orders = $this->cache->get("orders.$customerId"); // returns mixed
    $sum = 0.0;
    foreach ($orders as $order) {
        $sum += $order->total;
    }
    return $sum;
}

入全景模式 退出全屏模式

于此运行PHPStan第七级。其通过。foreachmixed->total之访问于mixed。PHPStan不能推理二者,因其无上限。运行时捕获之:Cannot access property total on mixed,然惟缓存未命中时方得之,此乃测试所不及也。

第八级,PHPStan之善止焉。及第八级,于mixed唤法,乃违。迭mixed,亦违。mixed[]之取,亦违。其解,在以型注缓存之裹:

final class OrderCache
{
    /** @return list<Order> */
    public function getOrders(int $customerId): array
    {
        $raw = $this->store->get("orders.$customerId");
        if (!is_array($raw)) {
            return [];
        }
        return array_map(fn($r) => Order::fromArray($r), $raw);
    }
}

入全屏模式 出全屏模式

PHPDoc之泛型与边界处之运行时校验,乃成其功。至于余下之代码库中,$cache->getOrders($id)乃一也list<Order>不可。mixed无空值引用。PHPStan级别8保持绿色。

失三:泛型包装器Collection<mixed>隐无物

Laravel(拉aravel)之Collection乃 Laravel 代码库中最常用之类,亦为最被欺瞒者。若不添 PHPDoc,其默认类型为Collection<int, mixed>

public function activeOrders(): Collection
{
    return Order::where('status', 'active')->get();
}

入全景模式 出全屏模式

PHPStan视之为Illuminate\Database\Eloquent\Collection<int, Model>至多耳Collection<mixed>若尔用基Collection锁链之呼下,皆运行于mixed

$activeOrders
    ->filter(fn ($o) => $o->customerId === $id)  // mixed->customerId
    ->map(fn ($o) => $o->total)                  // mixed->total
    ->sum();

入全景模式 出全景模式

凡矢皆为臆测。凡重构(更名customerIdcustomer_id,于?total)皆默失此呼点。

其解乃PHPDoc之泛:

/** @return Collection<int, Order> */
public function activeOrders(): Collection
{
    return Order::where('status', 'active')->get();
}

入全景模式 出全景模式

PHPStan與PHPStorm皆遵此註解。->filter之回調今得有型之Order->mapCollection<int, float>->sum乃型之float。型之信息流於管線。

larastan/larastan使此效於Eloquent集合於六級以上。其難在:汝必自書泛型。其預設(一Collection(无注解)即Collection<mixed>。此乃众代码库所栖之地。

失败之四:重构于下游三处崩坏

一微小更名,足显累积之费。汝有一getConfig()之助函数,返mixed

function getConfig(string $key): mixed
{
    return config($key);
}

// Three callers, all using it differently:
$ttl = getConfig('cache.default.ttl');              // expects int
$drivers = getConfig('queue.connections');          // expects array
$timeout = getConfig('http.timeout');               // expects ?int

全屏模式入 全屏模式出

汝决之getConfig甚愚(盖config()已存也),宜删助件。IDE之重命名重构,不识诸调用处之殊,以其皆耗mixed也。尔删助件,调用处遂直归config(),然三者今用其值,非其所宜。

尤甚:尔复欲增验。getConfig 当失其键则应抛之:

function getConfig(string $key): mixed
{
    $value = config($key);
    if ($value === null) {
        throw new ConfigMissing($key);
    }
    return $value;
}

全屏模式 退出全屏模式

今凡用?int于此函数者,其约已然更易。PHPStan莫能助,盖mixed既纳int亦纳?int。诸试皆通。然生产之境,每于部署后首遇缓存未命中而抛之。

此非泛型之弊,乃小而专之型助也。

final class CacheConfig
{
    public function ttl(string $store): int
    {
        $value = config("cache.stores.$store.ttl");
        if (!is_int($value)) {
            throw new ConfigMissing("cache.stores.$store.ttl");
        }
        return $value;
    }
}

入全屏模式 出全屏模式

不可重构型约,破型系统则不可。此即型之要义也。

失败五:mixed于型约方法签中,必使每用者扩型。

此微妙。一法有mixed返签之毒,害及诸班。

trait HasMetadata
{
    public function getMetadata(string $key): mixed
    {
        return $this->metadata[$key] ?? null;
    }
}

final class Order
{
    use HasMetadata;

    public function shippingAddress(): Address
    {
        $raw = $this->getMetadata('shipping');  // mixed
        return Address::fromArray($raw);        // PHPStan: ok, mixed accepts array
    }
}

入全景模式 出全屏模式

PHPStan等级八标志Address::fromArray($raw)故也$raw是。mixedfromArray期之array欲绝其声,则神人加以运行时之变。assert(is_array($raw))固可行,然为类型系统之失,实乃运行时之税也。

其真解,在于去mixed于特性,以PHPDoc使特性为泛型:

/**
 * @template TValue
 */
trait HasMetadata
{
    /** @var array<string, TValue> */
    private array $metadata = [];

    /** @return TValue|null */
    public function getMetadata(string $key)
    {
        return $this->metadata[$key] ?? null;
    }
}

final class Order
{
    /** @use HasMetadata<string|array<string, string>> */
    use HasMetadata;
}

入全屏模式 出全屏模式

PHPStan敬重@template于特性。消费者固类型。mixed 已绝于调用之域,无运行时之断言。

之性,mixed 积聚最速,盖因其框架之质,复用之谓也。每复用,弱之签章随之流布。

更替之阶:mixed 而至联合,而至界面,而至具体之型

当于码中见 mixed,循此阶而上之:

  1. 具体类型:若能书Order,则当书Order。毋甘于不足
  2. 。可空具体类型?Order,若法可合法地返回无物
  3. 。联合类型Order|Refund|null 每当函数返回若干物时。别异之合乃PHP之谓也。"此中择一"
  4. 接口Refundable 若重行止,非重类之别
  5. PHPDoc泛型list<Order>array<string, Money>Collection<int, Order>。PHPStan洞悉此理。
  6. mixed唯于信界之畔(反序列化JSON,自中读取)$_POST,先击中缓存而后验之。继而立时收束,勿使值流他处。

经验之则:mixed宜存于汝之码库,仅一语耳。其后之语,当渐狭。

五十行审计脚本,寻汝之最恶mixed罪人

初度无需PHP-Parser。以适切之式grep,已得九成功矣。此式录为bin/audit-mixed.sh

#!/usr/bin/env bash
# audit-mixed.sh: rank files by mixed-density
set -euo pipefail

ROOT="${1:-src}"
TMP=$(mktemp)

# count mixed occurrences per file, ignoring vendor/tests
find "$ROOT" -name '*.php' \
    -not -path '*/vendor/*' \
    -not -path '*/tests/*' \
    -print0 \
  | while IFS= read -r -d '' file; do
      # match `: mixed` returns, `mixed $var` params, `@param mixed`,
      # `@return mixed`, and `@var mixed` PHPDoc tags
      count=$(grep -cE '(\:\s*mixed\b|mixed\s+\$|@(param|return|var)\s+mixed)' "$file" || true)
      lines=$(wc -l < "$file")
      if [ "$count" -gt 0 ]; then
          # density = mixed count per 100 lines
          density=$(awk "BEGIN { printf \"%.2f\", ($count * 100) / $lines }")
          printf '%s\t%d\t%d\t%s\n' "$density" "$count" "$lines" "$file" >> "$TMP"
      fi
  done

echo "DENSITY  COUNT  LINES  FILE"
echo "-----------------------------"
sort -rn "$TMP" | head -30
rm "$TMP"

入全景模式 出全景模式

运行之:./bin/audit-mixed.sh src。得三十最劣者,依mixed-每百行密度排序。密度重于原数。二千行之模,八mixed 之康健,逾于五十行之简牍,兼有六

。若论 PHP-Parser 之版本(精微,察动态属性之取用,略去注解),则 nikic/php-parser 5.x 之外,更添一 NodeVisitorAbstract ,能数 Identifier 节点中,凡名唤 mixed 者于 Stmt\ClassMethod 之内,而Stmt\Function_。bash之版式,乃CI检之所需。增行,使构建若顶文件之密度越阈,则败之(初定3.0,月降之)。

PHPStan级8,乃执行之严。

审计示汝所处。PHPStan级8,使汝恒守之。

phpstan.neon

parameters:
    level: 8
    paths:
        - src
    ignoreErrors: []
    treatPhpDocTypesAsCertain: true
    checkMissingIterableValueType: true
    checkGenericClassInNonGenericObjectType: true

入全景模式 退出全屏模式

第八级禁止:

  • 方法调用于mixed
  • 属性访问于mixed
  • 数组访问于mixed
  • 遍历于mixed
  • 从函数返回mixed而其声明的返回更为具体

它允许mixed 自诩为既定之型(PHP 许之,PHPStan 亦不与之争)。然则必先收束,而后得用。合以审计之脚本,则渐进而无止:PHPStan 阻止新 mixed 以不安全之法加之;审计之压力,迫使团队删去旧者。

吾所言之团队,已迁 1,400。mixed 返归二百八十之数于四分之一。其HTTP五百之率因类型错误而降至近于无。PHPStan之基线文件每周皆有所减。无人增新验证之码。彼等唯书旧有之文而已。

mixed 乃最易书之类型。然持之则最费。

君尝承最恶之 mixed 耶?弃其密度之率而audit-mixed.sh 言于注脚.


mixed 之弊,乃症结也:框架之常,恃动态之变,渗入域码,非其所宜。其解,在框架层与类型核心间,界之清明耳. 脱节之PHP者,汝之代码基架,逾越框架之常则,所求之架构层也:仓库之接口,值物之对象,永不见mixed之用例是也。

Decoupled PHP — Clean and Hexagonal Architecture for Applications That Outlive the Framework

。Kindle、Paperback、Hardcover皆可购。英文、德文、日文版已出,葡萄牙文、西班牙文版将即至。