excel让日期+1天,时间上下30分钟以内的公式
=DATE(YEAR(M4),MONTH(M4),DAY(M4)+1)+TIME(HOUR(M4),MINUTE(M4)+RANDBETWEEN(-30,30),SECOND(M4)) =DATE(YEAR(M4),MONTH(M4),DAY(M4)+1)+TIME(HOUR(M4),MINUTE(M4)+RANDBETWEEN(-30,30),SECOND(M4)) 推荐阅读:https://zh.javascript.info/import-export
export导出
我们可以通过在声明之前放置 export 来标记任意声明为导出,无论声明的是变量,函数还是类都可以。
例如,这里的所有导出均有效:
// 导出数组
export let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// 导出 const 声明的变量
export const MODULES_BECAME_STANDARD_YEAR = 2015;
// 导出类
export class User {
constructor(name) {
this.name = name;
}
}导出与声明分开
另外,我们还可以将 export 分开放置。
下面的例子中,我们先声明函数,然后再导出它们:
function sayHi(user) {
alert(`Hello, ${user}!`);
}
function sayBye(user) {
alert(`Bye, ${user}!`);
}
export {sayHi, sayBye}; // 导出变量列表Import *
通常,我们把要导入的东西列在花括号 import {...} 中,就像这样:
import {sayHi, sayBye} from './say.js';
sayHi('John'); // Hello, John!
sayBye('John'); // Bye, John!如果有很多要导入的内容,我们可以使用 import * as
import * as say from './say.js';
say.sayHi('John');
say.sayBye('John');Import “as” 和 Export “as”
我们也可以使用 as 让导入具有不同的名字。
import {sayHi as hi, sayBye as bye} from './say.js';
hi('John'); // Hello, John!
bye('John'); // Bye, John!export {sayHi as hi, sayBye as bye};现在 hi 和 bye 是在外面使用时的正式名称
Export default 导出的是一个模块
在实际中,主要有两种模块。
包含库或函数包的模块,像上面的 say.js。
声明单个实体的模块,例如模块 user.js 仅导出 class User。
大部分情况下,开发者倾向于使用第二种方式,以便每个“东西”都存在于它自己的模块中。
当然,这需要大量文件,因为每个东西都需要自己的模块,但这根本不是问题。实际上,如果文件具有良好的命名,并且文件夹结构得当,那么代码导航(navigation)会变得更容易。
模块提供了一个特殊的默认导出 export default 语法,以使“一个模块只做一件事”的方式看起来更好。
将 export default 放在要导出的实体前:
export default class User { // 只需要添加 "default" 即可
constructor(name) {
this.name = name;
}
}每个文件应该只有一个 export default:
……然后将其导入而不需要花括号:
import User from './user.js'; // 不需要花括号 {User},只需要写成 User 即可
new User('John');不用花括号的导入看起来很酷。刚开始使用模块时,一个常见的错误就是忘记写花括号。所以,请记住,import 命名的导出时需要花括号,而 import 默认的导出时不需要花括号。
命名导出
由于每个文件最多只能有一个默认的导出,因此导出的实体可能没有名称。
例如,下面这些都是完全有效的默认的导出:
export default class { // 没有类名
constructor() { ... }
}
export default function(user) { // 没有函数名
alert(`Hello, ${user}!`);
}
// 导出单个值,而不使用变量
export default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];不指定名称是可以的,因为每个文件只有一个 export default,因此不带花括号的 import 知道要导入的内容是什么。
如果没有 default,这样的导出将会出错:
export class { // Error!(非默认的导出需要名称)
constructor() {}
}“default” 名称
在某些情况下,default 关键词被用于引用默认的导出。
例如,要将函数与其定义分开导出:
function sayHi(user) {
alert(`Hello, ${user}!`);
}
// 就像我们在函数之前添加了 "export default" 一样
export {sayHi as default};或者,另一种情况,假设模块 user.js 导出了一个主要的默认的导出和一些命名的导出(这种情况很少见,但确实会发生):
export default class User {
constructor(name) {
this.name = name;
}
}
export function sayHi(user) {
alert(`Hello, ${user}!`);
}这是导入默认的导出以及命名的导出的方法:
import {default as User, sayHi} from './user.js';
new User('John');如果我们将所有东西 * 作为一个对象导入,那么 default 属性正是默认的导出:
import * as user from './user.js';
let User = user.default; // 默认的导出
new User('John');命名的导出会强制我们使用正确的名称进行导入:
import {User} from './user.js';
// 导入 {MyUser} 不起作用,导入名字必须为 {User}……对于默认的导出,我们总是在导入时选择名称:
import User from './user.js'; // 有效
import MyUser from './user.js'; // 也有效
// 使用任何名称导入都没有问题重新导出
“重新导出(Re-export)”语法 export ... from ... 允许导入内容,并立即将其导出(可能是用的是其他的名字),就像这样:
export {sayHi} from './say.js'; // 重新导出 sayHi
export {default as User} from './user.js'; // 重新导出 default重新导出默认导出
重新导出时,默认导出需要单独处理。
假设我们有一个 user.js 脚本,其中写了 export default class User,并且我们想重新导出类 User:
export default class User {
// ...
}我们可能会遇到两个问题:
要重新导出默认导出,我们必须明确写出 export {default as User},就像上面的例子中那样。
如果我们想将命名的导出和默认的导出都重新导出,那么需要两条语句:
export * from './user.js'; // 重新导出命名的导出
export {default} from './user.js'; // 重新导出默认的导出动态导入
下面这样的 import 行不通:
import ... from getModuleName(); // Error, only from "string" is allowed
if(...) {
import ...; // Error, not allowed!
}
{
import ...; // Error, we can't put import in any block
}import() 表达式
let modulePath = prompt("Which module to load?");
import(modulePath)
.then(obj => <module object>)
.catch(err => <loading error, e.g. if no such module>)
//或者导入 say 中的两个函数
let {hi, bye} = await import('./say.js');
hi();
bye();
//或者 默认的导出
let obj = await import('./say.js');
let say = obj.default;
// or, in one line: let {default: say} = await import('./say.js');
say();
尽管 import() 看起来像一个函数调用,但它只是一种特殊语法,只是恰好使用了括号(类似于 super())。
因此,我们不能将 import 复制到一个变量中,或者对其使用 call/apply。因为它不是一个函数。
PCNTL(PHP Control Process)是 PHP 的一个扩展,主要用于进程控制和信号处理,仅在 UNIX/Linux 系统(Windows 不完全支持)的 CLI 模式下有效。它提供了与操作系统底层进程管理相关的功能,常用于开发守护进程、任务调度、异步处理等场景。
| 功能分类 | 具体能力 | 典型场景 |
|---|---|---|
| 进程创建 | pcntl_fork() 创建子进程,实现多进程并发处理 | 高并发任务处理、分布式计算 |
| 进程等待与回收 | pcntl_wait()/pcntl_waitpid() 等待子进程结束并回收资源,避免僵尸进程 | 守护进程管理、任务调度系统 |
| 信号处理 | pcntl_signal() 注册信号处理器,响应系统信号(如 SIGINT、SIGTERM 等) | 程序优雅退出、异步事件处理 |
| 进程间通信 | 配合 sysvmsg/sysvsem 等扩展实现进程间数据交换 | 微服务通信、分布式任务队列 |
| 进程状态控制 | pcntl_exec() 替换当前进程的内存空间,执行外部程序 | 动态加载程序、命令行工具集成 |
编译时安装(适用于源码安装 PHP):
./configure --enable-pcntl [--with-pcntl=DIR]
make && make install包管理器安装(适用于 Linux 系统):
sudo apt-get install php-pcntlsudo yum install php-pcntl(或通过 Remi 源)php -m | grep pcntl,若输出 pcntl 则表示扩展已启用。pcntl_fork)<?php
// fork_demo.php - 多进程示例
echo "主进程启动 (PID: " . posix_getpid() . ")\n";
$pid = pcntl_fork();
if ($pid === -1) {
die("fork 失败: " . pcntl_strerror(pcntl_get_last_error()));
} elseif ($pid === 0) {
// 子进程逻辑
echo "子进程启动 (PID: " . posix_getpid() . ")\n";
echo "子进程工作中...\n";
sleep(2);
echo "子进程完成任务,退出\n";
exit(0);
} else {
// 父进程逻辑
echo "父进程继续执行,子进程PID: $pid\n";
// 等待子进程结束并回收资源
$status = 0;
$childPid = pcntl_wait($status);
echo "子进程 $childPid 已退出,状态码: " . pcntl_wexitstatus($status) . "\n";
}执行结果:
主进程启动 (PID: 1000)
父进程继续执行,子进程PID: 1001
子进程启动 (PID: 1001)
子进程工作中...
子进程完成任务,退出
子进程 1001 已退出,状态码: 0pcntl_signal)<?php
// signal_demo.php - 信号处理示例(需 CLI 运行)
function signalHandler($signal) {
switch ($signal) {
case SIGINT: // Ctrl+C
echo "\n接收到 SIGINT,准备退出...\n";
exit(0);
case SIGUSR1: // 自定义信号1
echo "\n接收到 SIGUSR1,执行日志刷新...\n";
// 此处可添加日志刷新逻辑
break;
default:
echo "\n接收到未知信号: $signal\n";
}
}
// 注册信号处理器
pcntl_signal(SIGINT, 'signalHandler');
pcntl_signal(SIGUSR1, 'signalHandler');
echo "程序运行中 (PID: " . posix_getpid() . ")\n";
echo "按 Ctrl+C 退出,或使用 kill -SIGUSR1 PID 发送信号\n";
// 保持程序运行
while (true) {
pcntl_signal_dispatch(); // 处理信号
sleep(1);
echo ".";
}测试方法:
Ctrl+C 触发 SIGINT,程序优雅退出。kill -SIGUSR1 <PID>,触发自定义信号处理。pcntl_exec)<?php
// exec_demo.php - 进程替换示例
echo "原进程 (PID: " . posix_getpid() . ") 执行中...\n";
$args = [
'ls', // 程序路径
'-la', // 参数1:长列表格式
'/etc' // 参数2:目标目录
];
// 替换当前进程为 ls 命令,原进程不再继续执行
pcntl_exec('/bin/ls', $args);
// 以下代码不会执行
echo "此句不会输出,进程已被替换\n";执行结果:
直接输出 /etc 目录的详细列表,原 PHP 进程被 ls 命令完全替换。
pcntl_signal 有限支持)。pcntl_fork 可能导致进程混乱,仅适用于 CLI 模式。pcntl_wait 回收子进程,避免僵尸进程堆积。sysvmsg、sysvsem 等扩展使用。通过 PCNTL 扩展,PHP 得以突破单进程限制,实现更接近系统底层的进程控制能力,是开发高性能后端服务的重要工具。
在PHP里,declare结构可对代码块的执行行为加以控制。它能够设定指令,对代码的运行环境或者解析方式产生影响。下面为你详细介绍其作用、用法以及相关注意事项。
declare主要用于以下方面:
strict_types、ticks)进行设定。 ticks和信号处理)。基础语法:
declare (指令1 [, 指令2 [, ...]]) {
// 代码块
}
// 或者单行形式(仅对下一条语句生效)
declare(指令); 语句;strict_types=1(严格类型模式)该指令会让函数的参数和返回值类型声明采用严格类型检查。
<?php
// 在文件顶部启用严格类型(影响整个文件)
declare(strict_types=1);
function sum(int $a, int $b): int {
return $a + $b;
}
// 下面这行会报错,因为5.5是浮点数,与int类型不匹配
echo sum(5, 5.5);关键点:
ticks=1(时钟周期)ticks能在执行指定数量的低级语句后,触发注册的回调函数。
<?php
// 每执行1条低级语句就触发一次register_tick_function
declare(ticks=1);
// 注册回调函数
register_tick_function(function() {
echo "执行了一个tick\n";
});
// 循环中的每条语句都会触发tick
for ($i = 0; $i < 2; $i++) {
echo "循环迭代\n";
}输出结果:
执行了一个tick
循环迭代
执行了一个tick
执行了一个tick
循环迭代
执行了一个tick
执行了一个tick应用场景:
encoding='编码格式'(字符编码)此指令用于声明文件的编码格式(不过在实际中很少使用)。
<?php
// 声明文件使用UTF-8编码
declare(encoding='UTF-8');文件级作用域:如果declare放在文件的全局作用域,那么它会对整个文件生效。
<?php
declare(strict_types=1); // 对整个文件生效
function test() { /* ... */ }代码块作用域:若declare包含在代码块中,就只对该代码块有效。
<?php
// 非严格模式
function add($a, $b) {
return $a + $b;
}
// 严格模式代码块
declare(strict_types=1) {
function multiply(int $a, int $b): int {
return $a * $b;
}
}单行声明:单行的declare仅对接下来的一条语句起作用。
<?php
declare(ticks=1); echo "这条语句会触发tick";
echo "这条语句不会触发tick";set_time_limit()的对比ticks可用于实现更精细的超时控制:
<?php
declare(ticks=1);
// 超时处理函数
function handle_timeout() {
throw new Exception("执行超时");
}
// 注册信号处理
pcntl_signal(SIGALRM, 'handle_timeout');
// 设置3秒后触发ALARM信号(仅在CLI模式下有效)
pcntl_alarm(3);
// 模拟耗时操作
while (true) {
usleep(100000);
}版本兼容性:
strict_types从PHP 7.0开始支持。ticks在PHP 5.3之前存在性能问题,之后的版本有所优化。性能影响:
ticks会使性能下降,因为每条低级语句都会触发回调。信号处理限制:
pcntl_alarm)仅在CLI模式下有效,在Web环境中无效。编码声明:
encoding指令主要用于文档说明,PHP默认使用UTF-8,通常无需特别声明。| 指令 | 用途 | 示例 |
|---|---|---|
strict_types=1 | 启用严格类型检查 | declare(strict_types=1); |
ticks=N | 每执行N条低级语句触发回调 | declare(ticks=1); register_tick_function(...) |
encoding='UTF-8' | 声明文件编码格式 | declare(encoding='UTF-8'); |
合理运用declare能够提升代码的健壮性、安全性和可维护性,特别是在需要严格类型控制或者复杂执行环境管理的场景中。
实现代码覆盖率分析。
进行性能监控。
处理异步信号(在 CLI 模式下)。分别举一个例子:
<?php
// coverage.php - 使用 ticks 实现简单的代码覆盖率分析
declare(ticks=1);
// 存储已执行的行号
$coveredLines = [];
// 注册 tick 函数,每次执行低级语句时记录行号
register_tick_function(function() use (&$coveredLines) {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
if (!empty($trace[0]['file']) && !empty($trace[0]['line'])) {
$file = $trace[0]['file'];
$line = $trace[0]['line'];
if (strpos($file, __FILE__) !== false) { // 只记录当前文件
$coveredLines[$line] = true;
}
}
});
// 示例函数
function calculateSum($a, $b) {
if ($a > 0) {
return $a + $b;
} else {
return $b;
}
}
// 测试用例
$result = calculateSum(5, 3);
// 输出覆盖率报告
echo "代码覆盖率分析结果:\n";
$source = file(__FILE__);
foreach ($source as $lineNum => $line) {
$realLineNum = $lineNum + 1;
$status = isset($coveredLines[$realLineNum]) ? "[✓]" : "[ ]";
echo "$status $realLineNum: $line";
}执行结果示例:
代码覆盖率分析结果:
[✓] 1: <?php
[✓] 2: // coverage.php - 使用 ticks 实现简单的代码覆盖率分析
[✓] 3: declare(ticks=1);
...
[✓] 11: $coveredLines[$line] = true;
[ ] 12: }
...
[✓] 19: function calculateSum($a, $b) {
[✓] 20: if ($a > 0) {
[✓] 21: return $a + $b;
[ ] 23: } else {
[ ] 24: return $b;
...<?php
// performance.php - 函数级性能监控
declare(ticks=1);
// 存储函数执行时间(微秒)
$functionTimers = [];
$currentFunction = null;
// 注册 tick 函数
register_tick_function(function() use (&$functionTimers, &$currentFunction) {
if ($currentFunction) {
$functionTimers[$currentFunction] += 1;
}
});
// 函数调用前记录开始时间
function startTimer($functionName) {
global $currentFunction;
$currentFunction = $functionName;
$GLOBALS['functionTimers'][$functionName] = 0;
}
// 函数调用后输出耗时
function endTimer($functionName) {
global $currentFunction;
$currentFunction = null;
echo "函数 {$functionName} 执行了 {$GLOBALS['functionTimers'][$functionName]} 个 tick\n";
}
// 示例函数
function processData() {
startTimer(__FUNCTION__);
for ($i = 0; $i < 1000; $i++) {
// 模拟耗时操作
sqrt($i);
}
endTimer(__FUNCTION__);
}
function fetchData() {
startTimer(__FUNCTION__);
usleep(500000); // 休眠500毫秒
endTimer(__FUNCTION__);
}
// 执行测试
processData();
fetchData();执行结果示例:
函数 processData 执行了 3005 个 tick
函数 fetchData 执行了 3 个 tick<?php
// signal.php - 异步信号处理示例(需在 CLI 模式运行)
declare(ticks=1);
// 注册信号处理函数
function signalHandler($signal) {
switch ($signal) {
case SIGINT: // Ctrl+C
echo "\n接收到 SIGINT 信号,优雅退出...\n";
exit(0);
case SIGTERM: // kill 命令
echo "\n接收到 SIGTERM 信号,清理资源...\n";
// 这里可以添加资源清理代码
exit(0);
case SIGALRM: // 定时器信号
echo "\n定时器触发,执行定时任务...\n";
pcntl_alarm(2); // 重新设置2秒后触发
break;
}
}
// 安装信号处理器
pcntl_signal(SIGINT, 'signalHandler');
pcntl_signal(SIGTERM, 'signalHandler');
pcntl_signal(SIGALRM, 'signalHandler');
// 设置定时器(2秒后触发 SIGALRM)
pcntl_alarm(2);
echo "程序运行中(PID: " . getmypid() . "),按 Ctrl+C 退出...\n";
// 主循环
while (true) {
// 处理待处理的信号
pcntl_signal_dispatch();
sleep(1);
echo ".";
}执行结果示例:
程序运行中(PID: 12345),按 Ctrl+C 退出...
..
定时器触发,执行定时任务...
..
定时器触发,执行定时任务...
^C
接收到 SIGINT 信号,优雅退出...注意事项:
在PHP 8.0及之后的版本中,引入了match表达式,它是一种更简洁、更强大的条件判断结构,可视为switch语句的升级版。下面为你详细介绍它的语法、特性以及与switch的差异。
$result = match (表达式) {
静态值1 => 返回值1, // ✅ 正确:左侧为静态值
静态值2, 静态值3 => 返回值2, // ✅ 多个静态值合并
表达式/函数调用 => 返回值4,
default => 默认返回值,
};关键点:
match是表达式,而非语句,可直接赋值给变量。===进行值的比较,需类型和值都一致。break:每个条件后无需中断,不会自动向下穿透。$num = 2;
$result = match ($num) {
1 => '一',
2 => '二',
3 => '三',
default => '其他',
};
// $result = '二'$day = 'Saturday';
$result = match ($day) {
'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday' => '工作日',
'Saturday', 'Sunday' => '周末',
default => '无效日期',
};
// $result = '周末'$age = 25;
$result = match (true) {
$age < 18 => '未成年人',
$age >= 18 && $age < 60 => '成年人',
default => '老年人',
};
// $result = '成年人'function getStatusCode() {
return 404;
}
$result = match (getStatusCode()) {
200 => '成功',
404 => '未找到',
500 => '服务器错误',
default => '其他错误',
};
// $result = '未找到'switch的核心差异| 特性 | match | switch |
|---|---|---|
| 比较方式 | 严格比较 (===) | 宽松比较 (==) |
| 是否返回值 | 是(表达式) | 否(语句) |
是否需要break | 否(不会穿透) | 是(默认穿透) |
| 支持表达式作为条件 | 是 | 否 |
| 示例 | $x = match (1) { 1 => 'a' }; | switch (1) { case 1: echo 'a'; } |
instanceof)$obj = new DateTime();
$result = match (true) {
$obj instanceof DateTime => '日期对象',
is_array($obj) => '数组',
default => '其他类型',
};
// $result = '日期对象'const APP_ENV = 'prod';
$result = match (APP_ENV) {
'dev' => '开发环境',
'test' => '测试环境',
'prod' => '生产环境',
default => '未知环境',
};
// $result = '生产环境'match 比较分支值,使用了严格比较 (===), 而 switch 语句使用了松散比较。
match 表达式会返回一个值。
match 的分支不会像 switch 语句一样, 落空时执行下个 case。
match 表达式必须彻底列举所有情况。
必须覆盖所有可能的值:若未提供default分支,且匹配失败,会抛出UnhandledMatchError异常。
$x = 4;
$result = match ($x) { // 缺少default,会报错
1 => '一',
2 => '二',
};match表达式从PHP 8.0开始支持,若使用的是PHP 7.x及以下版本,需改用switch语句或其他替代方案。
match表达式通过更简洁的语法、严格比较和直接返回值的特性,提升了代码的可读性和安全性,尤其适合复杂的条件判断场景。不过,在使用时要注意覆盖所有可能的情况,并了解它与switch的差异。