常用的.gitignore文件写法

这些条目,用到了再添加,不用一开始就添加很多条目,因为不一定用到,过早优化是万恶之源。

# 临时和备份文件
*.tmp
*.bak
*.swp
*~

# 操作系统相关
.DS_Store
Thumbs.db
ehthumbs.db

# 开发工具(IDE)
/.fleet
/.zed
/.vscode
/.settings
/.buildpath
/.project
/.idea
*.iml
*.iws
.idea_modules/
atlassian-ide-plugin.xml
crashlytics.properties
fabric.properties
.bootstrap/cache

# 构建和存储
out/
/public/build
/public/hot
/public/storage
/storage/*.key

# PHP
.phpactor.json
.phpunit.result.cache
/.phpunit.cache
.php-cs-fixer.cache
.phpstan.cache
rector.cache

# Composer
composer.lock
composer.phar
vendor/
auth.json

# npm
package-lock.json
yarn.lock
npm-debug.log
yarn-error.log
node_modules/

# Homestead开发环境
Homestead.json
Homestead.yaml

# 项目环境
.env
.env.backup
.env.production

# 日志
debug.log
error.log
*.log

# 测试
*.sqlite       # 本地测试数据库
/tests/_output # 测试输出目录


###### 操作系统文件 ######
.AppleDouble
.LSOverride
._*
.Spotlight-V100
.Trashes
.TemporaryItems
.fseventsd
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
.history

# Windows temporary files
Thumbs.db
ehthumbs.db

# Mac temporary files
.DS_Store


###### Directory for temporary files ######
.tmp/
.temp/
tmp/
temp/
caches/
cache/

modules/

###### compiled files and directories ######
*.com
*.class
*.dll
*.exe
*.o
*.so
/dist
/build


###### Download and upload directory ######
upload/
download/
uploadfiles/
downloadfiles/


###### 压缩包 ######
# Git 自带压缩,如果这些压缩包里有代码,建议解压后 commit
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip

###### 日志文件和数据库 ######
*.log
*.sqlite
*.db

###### 常见IDE和编辑器 ######
*.sublime*
__debug_bin
.project
.env
.env.backup
.env.production
/.fleet
/.idea
/.vscode
/.zed
/public/build
/public/hot
/public/storage
/public/**/*.map
/storage/*.key
/vendor
/go_build_*
out/

###### 前端工具链 ######
.sass-cache/*
node_modules/
npm-debug.log
yarn-error.log


###### PyCharm ######
# Created by https://www.gitignore.io/api/pycharm
# Edit at https://www.gitignore.io/?templates=pycharm

### PyCharm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml

# Gradle
.idea/**/gradle.xml
.idea/**/libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn.  Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

### PyCharm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721

# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr

# Sonarlint plugin
.idea/sonarlint

# End of https://www.gitignore.io/api/pycharm


###### Go ######
# `go test -c` 生成的二进制文件
*.test
# go coverage 工具
*.out
*.prof
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*


###### Python ######
# Created by https://www.gitignore.io/api/python
# Edit at https://www.gitignore.io/?templates=python

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# End of https://www.gitignore.io/api/python


###### Php Laravel ######

# Created by https://www.gitignore.io/api/laravel
# Edit at https://www.gitignore.io/?templates=laravel

### Laravel ###
/vendor/
node_modules/
npm-debug.log
yarn-error.log

# Laravel 4 specific
bootstrap/compiled.php
app/storage/

# Laravel 5 & Lumen specific
public/storage
public/hot
storage/*.key
.env
Homestead.yaml
Homestead.json
/.vagrant
.phpunit.result.cache

auth.json

# End of https://www.gitignore.io/api/laravel


###### Node.js ######

# Created by https://www.gitignore.io/api/node
# Edit at https://www.gitignore.io/?templates=node

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# next.js build output
.next

# nuxt.js build output
.nuxt

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# End of https://www.gitignore.io/api/node

ThinkPHP踩坑记录之数据库篇

虽然这么多年来ThinkPHP框架已经更新到大版本8了,越来越接近Laravel框架了,但是ThinkPHP 8框架还是有很多坑,我在工作中遇到不少,在此记录一下,以警醒后来人。

第1个坑,使用Db门面的inserA()批量插入数据到Oracle报错

因为ThinkPHP生成的批量插入SQL语句不符合Oracle语法。

避开这个坑的一个方法是,使用循环语句一条条地插入数据到Oracle,不要使用inserA()批量插入数据。

第2个坑,使用Think ORM操作Oracle 11g报错:SQLSTATE[HY000]: General error: 972 OCIStmtExecute: ORA-00972: identifier is too long

Oracle 11g的标识符的名字的长度不能超过30字节,标识符包括表名、视图名、序列名或触发器名。Oracle后续版本把标识符的名字的长度上限提高到了128字节。

ThinkPHP ORM 在处理 Oracle 时,有一个内部规范化逻辑(在 think-orm/src/db/connector/Oracle.phpBuilder 类中),会把表名/字段名转为小写+ 下划线风格(snake_case),以兼容 Oracle 的大小写不敏感特性。但当表名本身就包含大写或下划线时,这种转换会产生超长的蛇形名称,例如,把VIEW_JW_SPECIALTYDIRECTION转换为v_i_e_w__j_w__s_p_e_c_i_a_l_t_y_d_i_r_e_c_t_i_o_n,从而触发 Oracle 的长度限制。

vcruntime140.dll与使用14.44构建的PHP 8.4不兼容的解决方法

我在Windows server 2012系统安装了php-8.4.13-Win32-vs17-x64-ThreadSafe,并把安装路径加入path环境变量,我打开一个CMD窗口,运行:

php -v

报错:

vcruntime140.dll与当前使用14.44构建的PHP不兼容

引起这个错误的原因是,PHP 8.4.13(Win32-vs17-x64-ThreadSafe)是用 Visual Studio 2022(VS17) 编译的,依赖 vcruntime140.dll 版本 14.4x(报错信息中的14.44是内部版本)。而 Windows Server 2012 系统里现有的 vcruntime140.dll 是旧版 14.13(通常来自 Visual C++ 2017 或更早的 2015-2019 Redistributable),PHP 启动时做版本检查失败,直接报不兼容错误。

这是 PHP Windows 官方构建从 8.4 开始切换到 VS17 后最常见的错误。

解决方案(推荐步骤,按顺序操作)
1 下载正确的 Visual C++ Redistributable
官方推荐(PHP 官方文档和 windows.php.net 明确要求):

这两个链接永远指向微软最新的 2015-2022 合并包。

2 安装 Redistributable

以 管理员身份 运行两个 exe。

如果已经安装过旧版,安装程序会提示“修复”或直接升级——请选择 修复/安装。

安装完成后重启服务器(或至少重启一下 Explorer.exe / 注销再登录)。

3 验证是否解决了这个问题
重新打开 管理员 CMD,执行:

php -v

应该正常显示 PHP 8.4.13 版本信息,不再报 vcruntime140.dll 不兼容错误。

注意事项

  • Windows Server 2012 虽然已于 2023 年 EOL,但 PHP 官方明确支持到 8.4+(PHP 手册写明最低 Windows 8 / Server 2012)。
  • 最新的 VC++ 2015-2022 Redistributable 仍然可以在 Windows Server 2012 上成功安装并升级 DLL(社区和 PHP 用户已验证)。
  • 如果安装程序提示“不支持此操作系统”,请先安装 Windows Server 2012 的最新补丁(通过 Windows Update),再尝试。

Good Code Really Is Its Own Best Documentation (But Let’s Be Real About It)

Steve McConnell nailed it back in Code Complete:

“Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’ Improve the code and then document it to make it even clearer.”

Everyone loves quoting the first half. Code review debates, Twitter threads, team Slack wars—someone inevitably drops “good code is its own best documentation” like it ends the argument. But almost nobody remembers (or wants to remember) the second half: the part where you’re supposed to actually refactor instead of slapping a comment on top.

From personal experience: I’ve lost count of how many times I’ve weaponized that quote when defending my own code (“see? no comment needed!”), and how many times I’ve cursed under my breath while trying to decipher someone else’s legendary, comment-free masterpiece.

The harsh truth most of us don’t want to admit: very few programmers (myself included) are consistently skilled enough to make code so self-explanatory that comments become truly redundant. We’ve all inherited code from “rockstar” engineers—the ones whose exploits are legendary in school projects, open-source repos, or company folklore. You finally get handed their codebase and… oof. Thousands of lines, zero comments. Magic public library calls with zero docs, or docs so vague they’re basically “it works, trust me.” You step on the same undocumented landmine three times, swear quietly, and the next time someone gushes about that dev’s brilliance, you just fake a smile and think: “Sure, buddy. Sure.”

The real debate isn’t “can the best code skip comments entirely?” (maybe a handful of 10x wizards can). The practical question for teams is: should we ever encourage the idea that “comments are optional”?

If you broadcast “comments can be omitted if the code is good enough,” what most people hear is “yay, I never have to write comments again.” Especially dangerous for juniors fresh out of college who’ve never even heard the word “documentation” outside a syllabus footnote. Tell them comments are skippable, and their code will be nowhere near self-documenting—it’ll just be undocumented garbage.

In a company setting, comments aren’t just nice-to-have; they’re a team survival mechanism. They affect every collaborator today, every future maintainer tomorrow, and yes, your own professional reputation when someone else has to read your six-month-old mess. So my personal stance hasn’t budged: comments are mandatory. The more thorough the better. Overdo it? Fine—better safe than sorry.

For solo work or personal projects, the “good code = best docs” mantra sounds great… until you open your own code from half a year ago with zero comments and think: “Who the hell wrote this garbage?!”

Moral of the story: Aim for crystal-clear code that minimizes the need for comments. But don’t use that ideal as an excuse to skip them. Write the damn comments. Your future self (and the poor soul who inherits your repo) will thank you.

好代码本身就是最好的文档

“好代码本身就是最好的文档。当你需要添加一个注释时,你应该考虑如何修改代码才能不需要注释。” 这是 Steve McConnell 说的。同样,大部分人都不知道,或者忘掉后面半句:Good code is its own best documentation. As you’re about to add a comment, ask yourself, “How can I improve the code so that this comment isn’t needed?” Improve the code and then document it to make it even clearer. 如果你是程序员,回想一下多少次跟别人讨论代码是不是必须要注释时,这句话被引用到;有很多次在写代码时喜爱这句话,又多少次改别人的代码时痛恨这句话。

还是从我个人的观察来看,对很多程序员来说,其编码能力还不足以达到“代码本身就是最好的文档”的地步,包括我自己。敝司招聘过很多顶尖的工程师,有传说中的各种杰出前辈,可能在各种学校、公司内部事迹广为流传。但若是你哪天继承了他的代码遗产,就会发现很多传说中的明星跌落凡尘。成百上千行没有注释,使用一个公共库函数时要么接口就根本没注释只能基本靠猜,要么即使注释也语焉不详让你踩到未注明的大坑。每到这个时候你心里总会暗暗骂娘,后面别人再谈到他的光辉事迹时,你跟随讪笑时心中暗自腹诽:“牛逼个锤子!”

但我想很多人争论的焦点是:“注释是不是不可省略的、要强制执行的?”即使个别人能力真能达到“代码本身就是最好的文档”的地步(我还没见过),我也不建议在团队中传播“注释可以省略”这一想法。因为如果你说“注释可以省略”,可能你会发现大家都理解和实践成“终于可以不写注释了”。如果一个刚刚大学毕业、脑袋里从来没有过 documentation 概念、从来没写过注释的新人进入公司,就“终于可以不写注释了”,那么我想他的代码会很难达到“代码本身就是最好的文档”这个级别。因为他根本没有机会懂得什么叫做 documentation。

在公司里,代码注释深远地影响着团队合作的每个人,以及软件生存期里所有的维护者,甚至会影响自己的职业声誉。所以无论别人怎么想,我对注释这个问题的答案始终是:“注释是不可省略的,越完善越好的,甚至强制执行矫枉过正也没关系的!”

对于个人而言,“好代码就是好文档”也是一句空话,不信的话,你半年后再来看看自己半年前写的没有注释和文档的代码,你肯定会发出感慨:“这写的是什么玩意?!”

参考

https://yangwenbo.com/articles/words-that-make-code-bad.html

谷歌并不直接惩罚“网站使用AI生成内容”这一行为

谷歌并不直接惩罚“AI生成”这一行为,但它会严厉惩罚“低质量、无用”的内容。


谷歌在官方文档 《关于 AI 生成内容的指南》 中明确表示:不管内容是谁写的,只要对用户有帮助,谷歌就欢迎。


但如果你打算通过 GenAI 大规模刷量,需要注意以下致命风险:

1 谷歌的“垃圾内容”识别系统 (Spam Policies)

谷歌拥有极其强大的 AI 识别系统来打击 “扩展内容制作” (Scaled Content Abuse)。

  • 风险:如果你每天自动生成数百篇没有任何深度、只是在重复互联网已知信息、逻辑生硬的文章,谷歌会将你的网站判定为“内容农场”。
  • 后果:整站降权,甚至从索引中完全剔除。

2 E-E-A-T 标准的挑战

谷歌评估内容的标准是 E-E-A-T(专业性、体验、权威性、可靠性)。

痛点:AI 生成的内容往往缺乏“体验 (Experience)”。它写不出“我昨天去某店亲身体验后的感受”。如果你的文章缺乏这种真实的人性化细节,很难在竞争激烈的关键词下获得高排名。

3 AI 检测器的影响

虽然谷歌不公开承认使用“AI检测器”作为排名算法,但实验证明:

  • 高度可预测性:完全不经修改的 AI 原文具有极高的统计学规律(Perplexity & Burstiness)。
  • SEO 隐患:第三方工具(如 Originality.ai)能轻易测出 AI 痕迹。当算法更新时,那些纯 AI 的网站往往是受打击的“重灾区”。

安全使用 GenAI 的策略(如何不降权)

  • AI 辅助,人工润色:让 AI 提供大纲或初稿,由你加入真实的案例、独特的观点和人类的语气。
  • 增加原创图片/视频:这是证明“我是真人”最有力的信号。
  • 解决用户需求:如果 AI 写的文章确实解决了用户的问题(停留时间长、跳出率低),谷歌反而会给你高排名。
  • 标注来源:如果是部分使用 AI 辅助,可以在声明中坦诚告知。

生成式人工智能(Generative AI, GenAI)对依赖广告获利的网站的影响

长期来看,AI 爬虫大规模抓取内容会对传统靠广告获利的网站产生显著的负面冲击。
这种影响主要体现在以下几个维度:

1 流量的“断头式”下跌(零点击现象)

传统的搜索引擎(如 Google、百度)是分发流量。用户搜到摘要,点击进入你的网站看详情,过程中触发广告,你获得收入。


而 AI 爬虫(如 GPTBot)是吞噬内容。ChatGPT 抓取你的文章后,会将其改写、总结并直接在对话框里回答给用户。


后果:用户在 AI 界面就拿到了答案,不再需要点击你的原始链接。没有了点击,就没有了 PV/UV,你的广告展现量(Impression)和点击量(CTR)会直接萎缩。

2 广告联盟的价值缩水

广告联盟(如 Google AdSense)的计费核心是“真人浏览”。


AI 不看广告:AI 爬虫抓取内容时,不会加载 JavaScript 广告脚本,更不会点击广告。
优质流量流失:原本会访问你网站的高质量用户转而使用 AI 辅助工具(如 Perplexity AI),导致你的广告展示受众变得廉价且稀少,直接降低你的 RPM(每千次展示收入)。

3 “数据喂养”后的竞争降维

如果你网站的文章具有独特性(如深度测评、独家攻略),AI 学习了你的逻辑后,会以“全知全能”的姿态免费提供给全球用户。


后果:你苦心经营的原创护城河被抹平。AI 生成内容的成本几乎为零,而你创作的成本很高。当 AI 能够提供 80% 类似的信息时,大部分用户不会再去翻阅博主的个人网站。

4 这种冲击目前的应对现状

为了保护收入,目前网站主主要采取以下几种手段:

  • 技术屏蔽:通过修改 robots.txt 明确禁止 GPTBot、CCBot 等 AI 爬虫抓取。你可以查看 OpenAI 的官方说明 来了解如何屏蔽。
  • 内容锁定:将核心优质内容放入“付费墙”或“会员可见”,防止被公开爬取。
  • 版权诉讼:大型媒体(如《纽约时报》)正在起诉 AI 公司,要求为其训练数据支付版权费。

核心建议
如果你的网站完全依赖公域流量+硬广,广告收入下滑几乎是必然趋势。建议考虑以下转型方向:

  • 私域流量化:通过公众号、邮件订阅(Newsletter)留住用户,不依赖搜索流量。
  • 内容产品化:不仅提供信息,还提供工具、咨询或实体产品,这些是 AI 暂时无法取代的。
  • 接入 AI 保护协议:关注如 World Media Association 等机构推动的 AI 内容授权补偿方案。

汉隆的剃刀 (Hanlon’s Razor):能解释为愚蠢的,就不要解释为恶意的

汉隆的剃刀 (Hanlon’s Razor):能解释为愚蠢的,就不要解释为恶意的。——罗伯特·汉隆 (Robert J. Hanlon)

这一原则表明,一个行为所产生的消极结果并不是恶意。相反,消极结果更有可能归咎于这些没有得到充分理解的行动或影响。

1990年,黑客字典收录了汉隆剃刀,将其注释为“墨菲定律版的奥卡姆剃刀”,汉隆的剃刀自此闻名。

歌德在《少年维特的烦恼》中著有意思相似的一句话:误解和忽视在世上所造成的混乱,比恶意和欺骗要多上许多。无论如何,后两者出现的频率都低得多。

英国前首席新闻秘书伯纳德·英厄姆爵士同样说过一段类似的话:许多记者沉醉在政府阴谋论中。我可以担保,如果他们支持的是“政府搞砸论”,报导就会更准确一些。

参考

https://github.com/nusr/hacker-laws-zh

https://en.wikipedia.org/wiki/Hanlon%27s_razor

https://zh.wikipedia.org/wiki/%E6%B1%89%E9%9A%86%E5%89%83%E5%88%80

Windows系统PHP8.4安装pdo_oci扩展的方法

PDO_OCI 扩展是PDO 的 Oracle 驱动,用于连接 Oracle 数据库,安装过程类似于安装OCI8扩展,但 PDO_OCI 的维护较少活跃(自 1.1.0 版后更新慢),且在 PHP 8.4 上可能需要手动编译或使用社区版本。

注意,PDO_OCI 功能不如 OCI8 完整(不支持某些高级绑定、LOB 流式等),生产环境建议优先 OCI8。

如果坚持用 PDO_OCI,可以从https://pecl.php.net/package/PDO_OCI/1.2.0/windows网站下载Windows版本的PDO_OCI扩展的官方预编译 DLL。注意下载的PDO_OCI扩展的版本要与你的PHP版本匹配(位数和是否线程安全)。例如8.4 Thread Safe (TS) x64,下载后得到php_pdo_oci-1.2.0-8.4-ts-vs17-x64.zip文件,解压后得到php_pdo_oci.dll文件。

安装PDO_OCI扩展

把下载得到的php_pdo_oci.dll复制到Windows系统PHP安装路径里的ext目录里,然后在php.ini文件中添加一下一行配置:

extension=php_pdo_oci

启用PDO_OCI扩展

PDO_OCI扩展底层基于Oracle Instant Client,Oracle Instant Client的安装方法见Windows系统PHP8.4安装oci8扩展的方法

验证PDO_OCI扩展是否安装成功

打开一个CMD窗口,执行:

php -m | findstr PDO_OCI

若输出:

PDO_OCI

就说明PDO_OCI扩展安装成功。

创建php测试脚本 test_oci.php:

<?php
$conn = oci_connect('system', '你的密码', '127.0.0.1:1521/orcl');  // 替换为你的连接字符串,如 SID 或服务名
if (!$conn) {
    $e = oci_error();
    echo "连接失败: " . $e['message'];
} else {
    echo "OCI8 连接成功!\n";
    oci_close($conn);
}

打开一个CMD窗口,运行:

php test_oci.php

或通过浏览器访问(如果用 Web 服务器),若输出:

OCI8 连接成功!

就说明PDO_OCI扩展安装成功。

Windows系统PHP8.4安装oci8扩展的方法

在 Windows 系统上为 PHP 8.4 安装 Oracle 扩展(主要是 OCI8,因为它功能最完整、社区支持最好,适合生产环境连接 Oracle 数据库,包括 11g),过程比 Linux 简单得多:不需要编译,直接下载预编译好的 DLL 文件 + 配置PATH环境变量 + 修改 php.ini 即可。

注意,PDO_OCI 扩展在 Windows 上也支持,但功能较弱(不支持某些高级绑定、LOB 流式等),生产环境强烈推荐 OCI8扩展。以下以 OCI8 扩展为例。

下载 Oracle Instant Client(必须)

Instant Client 是 Oracle 的免费客户端库,OCI8 DLL 需要它才能运行。

1 下载Instant Client

访问 Oracle 官网下载页https://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html,选择 最新版本(推荐 23.x 或 21.x/19.x,兼容 Oracle 11g)。

下载 Basic 或 Basic Light ZIP 包(例如instantclient-basic-windows.x64-23.26.1.0.0.zip)。

可选下载 SDK 包,如果需要编译其他东西,但预编译 DLL 不需要下载 SDK 包。

2 解压到目录

例如

C:\oracle\instantclient_23_0

注意,路径不要有空格、中文,推荐放在 C 盘根目录或 Programs 下。

3 添加环境变量(非常重要,否则运行时找不到库)

右键“此电脑” → 属性 → 高级系统设置 → 环境变量。在“系统变量”中找到 Path(或新建),点击编辑 → 新建 → 添加:

C:\oracle\instantclient_23_0

点击确定保存。

重启电脑 或重启命令提示符/PowerShell,让 PATH 生效。

4 安装 Visual C++ Redistributable

下载最新版 Microsoft Visual C++ Redistributable(x64):

https://aka.ms/vs/17/release/vc_redist.x64.exe

安装后重启电脑。

下载预编译的 OCI8 DLL

Windows 上不推荐用 pecl install oci8(因为 Windows 没有编译环境),直接用 PECL 官网提供的 DLL。

1 访问 PECL OCI8 页面

https://pecl.php.net/package/oci8

点击最新稳定版(如 3.4.1 或更高,2025-2026 年最新版支持 PHP 8.3/8.4)。

2 在页面向下滚动,找到 DLL 的下载链接

选择 PHP 8.4 TS(Thread Safe,如果你的 PHP 是 Thread Safe 版)或 NTS(Non-Thread Safe)。

如何判断你的 PHP 是 TS 还是 NTS?参见Windows系统Thread Safe版本和Non Thread Safe版本的PHP的区别

下载对应的 ZIP(如php_oci8-3.4.1-8.4-ts-vs17-x64.zip,注意如果你的PHP是x86版本的,那么应该下载php_oci8-3.4.1-8.4-ts-vs17-x86.zip)。

3 复制oci DLL文件到PHP扩展目录

解压 ZIP 文件,得到 php_oci8.dll(或 php_oci8_12c.dll、php_oci8_19c.dll 等,根据 Instant Client 版本命名)。

复制这个 DLL 到你的 PHP 扩展目录,例如C:\php\ext\(即你的 PHP 安装路径下的 ext 文件夹)。

配置 php.ini

找到 php.ini 文件(通常在 PHP 安装目录,如 C:\php\php.ini)。 用记事本或 VS Code 打开,找到 [ExtensionList] 或搜索 “extension=”。根据你下载的 DLL 名添加一行:

extension=php_oci8_19

保存 php.ini。

重启 Web 服务器:

  • IIS:重启 IIS(iisreset)。
  • Apache/XAMPP/WAMP:重启服务。
  • 关闭所有命令提示符窗口,重新打开。

验证oci扩展是否安装成功

1 打开一个CMD窗口,运行:

php -m | findstr oci8

应该输出 oci8(无警告)。

2 创建php测试脚本 test_oci.php:

<?php
$conn = oci_connect('system', '你的密码', '127.0.0.1:1521/orcl');  // 替换为你的连接字符串,如 SID 或服务名
if (!$conn) {
    $e = oci_error();
    echo "连接失败: " . $e['message'];
} else {
    echo "OCI8 连接成功!\n";
    oci_close($conn);
}

打开一个CMD窗口,运行:

php test_oci.php

或通过浏览器访问(如果用 Web 服务器)。

常见问题及解决方法

1 无法加载 php_oci8.dll(The specified module could not be found)

解决方法是,确认 Instant Client 的 Path 已添加到系统环境变量,且重启电脑。

2 DLL 版本与PHP版本必须匹配

解决方法是,Instant Client 19c+ 用 php_oci8_19c.dll 或 php_oci8.dll。

3 DLL 加载失败缺少 VC++ Redist

解决方法是,重新安装 Visual C++ Redistributable x64。

4 Warning: PHP Startup: Unable to load dynamic library ‘php_oci8_19’ (tried: ext\php_oci8_19 (%1 不是有效的 Win32 应用程序。), ext\php_php_oci8_19.dll (找不到指定的模块。)) in Unknown on line 0

这个错误的原因是,dll位数(x86或x64)与PHP的位数(x86或x64)不匹配。根据“不是有效的 Win32 应用程序”这个报错信息,解决方法是,下载x64版本的dll重新安装一下。