编程范式全貌概要-免费

引言:经常在一些社区上看到莫名的一句话PHP是世界上最好的语言,在语言的争论上,确实是大有华山论剑的门派之争。本文将通过编程语言的范式角度,来了解整个编程语言的发展史,同时更清晰的认知编程范式,以达到知己知彼,百战不殆

  编程语言发展到今天,出现了好多不同的代码编写方式,但不同的方式解决的都是同一个问题,那就是如何写出更为通用、更具可重用性的代码或模块。

一、从C语言谈起

  C语言的历史悠久,自其问世以来,其影响了太多太多的编程语言,到现在还一直被广泛使用,不得不佩服它的生命力。但是,我们也要清楚的知道,大多数的使用C语言来做内核的编程语言其实都是在改善C语言带来的问题+时代发展带来的变化。
下面来简单回顾下C语言的特性:

  1. C 语言是一个静态弱类型语言,在使用变量时需要声明变量类型,但是类型间可以有隐式转换;
  2. 不同的变量类型可以用结构体(struct)组合在一起,以此来声明新的数据类型;
  3. C 语言可以用 typedef 关键字来定义类型的别名,以此来达到变量类型的抽象;
  4. C 语言是一个有结构化程序设计、具有变量作用域以及递归功能的过程式语言;
  5. 通过指针,C 语言可以容易地对内存进行低级控制,然而这加大了编程复杂度;
  6. 编译预处理让 C 语言的编译更具有弹性,比如跨平台。

然而,在代码组织和功能编程上,C语言的上述,却不那么美妙了,eq:

//一个简单的交换两个变量的函数
void changeVar(int* x, int* y)
{
    int tmp = *x;
    *x = *y;
    *y = tmp;
}

可以想一想,这里为什么要用指针呢?因为如果不用指针的话,只是传进来的行参,即函数的形参是调用实参的一个拷贝,函数闭包里对形参的修改无法影响实参的结果。

然而,这个函数最大的问题是int tmp = *x决定了这个函数只能给int值使用,但是还有好多类型也等着能被调用呢,eq:double、float、string等,这就是个静态语言最糟糕的问题。

当然,这个时候大家都会想到类型转换,然而对于C语言的类型转换,是会出现很多问题的。

  • 比如:一个 double a[10] 的数组,a[2] 意味着 a + sizeof(double) * 2。如果你把 a 强转成 int,那么 a[2] 就意味着 a + sizeof(int) * 2。我们知道 sizeof(double) 是 8,而 sizeof(int) 是 4。于是访问到了不同的地址和内存空间,这就导致程序出现严重的问题。下面这种使用临时交换数据的buffer拷贝方案可以去掉类型转换时导致的地址变换:
//加入泛型变量的交换两个变量的函数
void changeVar(void* x, void* y, size_t size)
{
    char tmp[size];  //交换数据时需要用的 buffer
    memcpy(tmp, y, size);                                          
    memcpy(y, x, size);
    memcpy(x, tmp, size);
    /**
    * 1.函数接口中增加了一个size参数,用了 void* 后,类型被“抽象”掉了
,编译器不能通过类型得到类型的长度了,所以,需要我们手动地加上一个类型长度的标识。
2.函数的实现中使用了memcpy()函数,因为类型被“抽象”掉了,所以不能用赋值表达式了,很有可能传进来的参数类型还是一个结构体,因此,为了要交换这些复杂类型的值,我们只能使用内存复制的方法了。
    */
}

除了上面使用void* 来做泛型,在C语言中,还可以用宏定义来做泛型,不过却会带来宏的字符串替换,导致代码膨胀,导致编译出的执行文件相对较大,此处感兴趣的可以去深入学习一下。

二、泛型编程
  • 泛型:即是指具有在多种数据类型上皆可操作的含义,与模板有些相似。
    1.举例:
    • C语言版本
long sum(int *a, size_t size) {
  long result = 0;  for(int i=0; i
  • C++版本

template
T sum(Iter pStart, Iter pEnd) {
  T result = 0;
  for(Iter p=pStart; p!=pEnd; p++) {
    result += *p;
  }
  return result;  
}
// 那个0假设了类型是int;
// 那个T假设了 Iter 中出来的类型是T。如果类型不一样,那么就会导致转类型的问题。
  • php版本
public function sum($a, $b) {return intval($a)+intval($b);}
  • js版本
function sum($x, $y) {return parseInt($x)+parseInt($y);}
  • 泛型编程的重要设计模式-迭代器
  1. 首先,一个迭代器需要和一个容器在一起,因为里面是对这个容器的具体的代码实现。
  2. 它需要重载一些操作符,比如:取值操作*、成员操作->、比较操作==和!=,还有遍历操作++,等等。
    3.然后,还要typedef一些类型,比如value_type,告诉我们容器内的数据的实际类型是什么样子。
  3. 还有一些,如begin()和end()的基本操作。日志的记录等。

三、类型系统和泛型的本质

0. 函数式编程

对于函数式编程来说,它只关心定义输入数据和输出数据相关的关系,数学表达式里面其实是在做一种映射(mapping),输入的数据和输出的数据关系是什么样的,是用函数来定义的。

特征:

  • stateless:函数不维护任何状态。函数式编程的核心精神是 stateless,简而言之就是它不能存在状态,打个比方,你给我数据我处理完扔出来。里面的数据是不变的。
  • immutable:输入数据是不能动的,动了输入数据就有危险,所以要返回新的数据集。
    优势:
  • 没有状态就没有伤害。
  • 并行执行无伤害。
  • Copy-Paste 重构代码无伤害。
  • 函数的执行没有顺序上的问题。
  • 确定性。所谓确定性,就是像在数学中那样,f(x) = y 这个函数无论在什么场景下,都会得到同样的结果,而不是像程序中的很多函数那样。同一个参数,在不同的场景下会计算出不同的结果,这个我们称之为函数的确定性。所谓不同的场景,就是我们的函数会根据运行中的状态信息的不同而发生变化。
1.基于原型的编程范式

讲到原型,相信大家的第一映像一定是JavaScript吧,来个小问题,大家有谁知道js的国际全称叫什么吗?

  • 每个对象都有一个 proto 的属性
  • 来看下原型链:

var a = {
  x: 10,
  calculate: function (z) {
    return this.x + this.y + z;
  }
};
 
var b = {
  y: 20,
  __proto__: a
};
 
var c = {
  y: 30,
  __proto__: a
};
 
// call the inherited method
b.calculate(30); // 60
c.calculate(40); // 80
proto_image
  • 再来看看

// 一种构造函数写法
function Foo(y) {
  this.y = y;
}
 
// 修改 Foo 的 prototype,加入一个成员变量 x
Foo.prototype.x = 10;
 
// 修改 Foo 的 prototype,加入一个成员函数 calculate
Foo.prototype.calculate = function (z) {
  return this.x + this.y + z;
};
 
// 现在,我们用 Foo 这个原型来创建 b 和 c
var b = new Foo(20);
var c = new Foo(30);
 
// 调用原型中的方法,可以得到正确的值
b.calculate(30); // 60
c.calculate(40); // 80
prototype2

再来重温一下上面的原型讲解实例


function Person(){}
var p = new Person();

Person.prototype.name = "Hao Chen";
Person.prototype.sayHello = function(){
    console.log("Hi, I am " + this.name);
}

console.log(p.name); // "Hao Chen"
p.sayHello(); // "Hi, I am Hao Chen"

在上面这个例子中:

  • 我们先生成了一个空的函数对象 Person();
  • 然后将这个空的函数对象 new 出另一个对象,存在 p 中;
  • 这时再改变 Person.prototype,让其有一个 name 的属性和一个 sayHello() 的方法;
  • 我们发现,另外那个 p 的对象也跟着一起改变了。

注意一下:

  • 当创建 function Person(){} 时,Person.proto 指向 Function.prototype;
  • 当创建 var p = new Person() 时,p.proto 指向 Person.prototype;
  • 当修改了 Person.prototype 的内容后,p.proto 的内容也就被改变了。
    小结:

通过上述可以看到,这种玩法就是一种委托的方式。在使用委托的基于原型的语言中,运行时语言可以“仅仅通过序列的指针找到匹配”这样的方式来定位属性或者寻找正确的数据。所有这些创建行为、共享的行为需要的是委托指针。

不像是基于类的面向对象语言中类和接口的关系,原型和它的分支之间的关系并不要求子对象有相似的内存结构,因为如此,子对象可以继续修改而无需像基于类的系统那样整理结构。还有一个要提到的地方是,不仅仅是数据,方法也能被修改。因为这个原因,大多数基于原型的语言把数据和方法提作“slots”。

这种在对象里面直接修改的玩法,虽然这个特性可以带来运行时的灵活性,我们可以在运行时修改一个 prototype,给它增加甚至删除属性和方法。但是其带来了执行的不确定性,也有安全性的问题,而代码还变得不可预测,这有点黑科技的味道了。因为这些不像静态类型系统,没有一个不可变的契约对代码的确定性有保证,所以,需要使用者来自己保证。

2. 面向对象与go语言的委托模式

OOP三大特性:封装、继承和多态。
OOD原则/SOLID原则:

  • OCP-Open/close principle:开/闭原则;Open for extension;Closed for modification;

比如加上一些策略模式、适配器模式、观察者模式等

  • DIP-Dependency Inversion Principle:依赖倒置原则
  • 高层模块不能依赖低层模块,而是大家都依赖于抽象;

  • 抽象不能依赖实现,而是实现依赖抽象。
    DIP 倒置了:

  • 模块或包的依赖关系

  • 开发顺序和职责

  • SRP-Single Responsibility Principle:单一职责原则,又被称为“内聚性原则(Cohesion)”,意为:

一个模块的组成元素之间的功能相关性。
一个类,只能有一个引起它的变化的原因。
一个职责是一个变化的原因。
违反的后果:

  • 脆弱性:switch case/if else 非常脆弱;对系统的改动会导致系统中和改动的地方无关的许多地方出现问题。出现新问题的地方与改动的地方没有概念上的关联。要修正这些问题又会引出更多的问题,从而使开发团队就像一只不停追逐自己尾巴的狗一样。
  • 不可移植性

设计臭味:糟糕的代码有哪些特点?

  • 僵硬-不易改变

僵化性(rigidity):很难对系统改动,因为每个改动都会迫使许多对系统其他部分的改动。如果单一的改动会导致依赖关系的模块中的连锁改动,那么设计就是僵化的,必须要改动的
模块越多,设计就越僵化。

  • 脆弱-只想改变A,结果B被意外破坏。

牢固性(Immobility): 很难解开系统的纠结,使之成为一些可在其他系统中重用的组件。设计中包含了对其他系统有用的部分,而把这些部分从系统中分离出来所需的努力和风险是巨大的。

  • 导致误用的陷阱-做错误的事比做正确的事更容易,引诱程序员破坏原有的设计。

粘滞性(Viscosity): 做正确的事情比做错误的事情要困难。

  • 面临一个改动的时候,开发人员常常会发现会有多种改动的方法。有的方法会保持系统原来的设计,而另外一些则会破坏设计,当那些可以保持系统设计的方法比那些破坏设计的方法跟难应用是,就表明设计具有高的粘滞性,作错误的事情就很容易。
  • 晦涩-代码难以理解,定义的变量名和注释都不能够见名知意。

晦涩性(Opacity):很难阅读、理解。没有很好的表现出意图。

  • 代码可以用清晰、富有表现力的方式编写,也可以用晦涩、费解的方式编写。一般说来,随着时间的推移,代码会变得越来越晦涩。
    不必要的复杂性(Needless Complexity):设计中包含有不具任何直接好处的基础结构。
    如果设计中包含有当前没有用的组成部分,他就含有不必要的复杂性。当开发人员预测需求的变化,并在软件中放置了处理那些潜在变化的代码时,常常会出现这种情况。
  • 过度设计、copy-paste代码

不必要的重复(Needless Repetition):设计中包含有重复的结构,而该重复的结构本可以使用单一的抽象进行统一。

  • 当 copy,cut,paste 编程的时候,这种情况就会发生。

面向对象优点:

  • 能和真实的世界交相辉映,符合人的直觉。
  • 面向对象和数据库模型设计类型,更多地关注对象间的模型设计。
  • 强调于“名词”而不是“动词”,更多地关注对象和对象间的接口。
  • 根据业务的特征形成一个个高内聚的对象,有效地分离了抽象和具体实现,增强了可重用性和可扩展性。
  • 拥有大量非常优秀的设计原则和设计模式。

面向对象缺点:

  • 代码都需要附着在一个类上,从一侧面上说,其鼓励了类型。
  • 代码需要通过对象来达到抽象的效果,导致了相当厚重的“代码粘合层”。
  • 因为太多的封装以及对状态的鼓励,导致了大量不透明并在并发下出现很多问题。

go语言的委托模式:


type Widget struct {
    X, Y int
}

type Label struct {
    Widget        // Embedding (delegation)
    Text   string // Aggregation
    X int         // Override 
}

func (label Label) Paint() {
  // [0xc4200141e0] - Label.Paint("State")
    fmt.Printf("[%p] - Label.Paint(%q)\n", 
      &label, label.Text)
}

由上面可知:

  • 我们声明了一个 Widget,其有 X和Y;
  • 然后用它来声明一个 Label,直接把 Widget 委托进去;
  • 然后再给 Label 声明并实现了一个 Paint() 方法。

label := Label{Widget{10, 10}, "State", 100}

// X=100, Y=10, Text=State, Widget.X=10
fmt.Printf("X=%d, Y=%d, Text=%s Widget.X=%d\n", 
  label.X, label.Y, label.Text, 
  label.Widget.X)
fmt.Println()
// {Widget:{X:10 Y:10} Text:State X:100} 
// {{10 10} State 100}
fmt.Printf("%+v\n%v\n", label, label)

label.Paint()

我们可以看到,如果有成员变量重名,则需要手动地解决冲突。

3. 来看看我们日常一段程序代码的抽象解构(见draw图),logic + control
  • 分离业务逻辑+控制逻辑,业务逻辑又可分为:校验业务类型,数据处理业务类型;数据处理业务类型又可分为:格式化组装数据处理类型、struct结构体枚举预处理CURD

  • 用过的经验,以编程方式类型区分:

    • 面向对象:委托、策略、桥接、修饰、IoC/DIP、MVC
    • 函数式编程:修饰、管道、拼装
  • Logic 部分才是真正有意义的(What)

  • Control 部分只是影响 Logic 部分的效率(How)

  • 一个是在解决数据和算法,一个是在解决逻辑和控制。

4. 逻辑编程范式
四、泛型思想日常编码中的引用
 self::INIT,
            '消息出列' => self::HANDLING,
            '处理失败' => self::FAIL,
            '处理成功' => self::SUCCESS,
            '忽略处理' => self::IGNORE
        ];
    }

    /**
     * 所有值
     *
     * @return array
     */
    public static function all()
    {
        return array_values(static::comment());
    }

    /**
     * list
     *
     * @return mixed
     */
    public static function allObj()
    {
        if (static::$logObj == null) {
            static::$logObj = [
                static::INIT => static::create(static::INIT, '消息入列', ''),
                static::HANDLING => static::create(static::HANDLING, '消息出列', ''),
                static::FAIL => static::create(static::FAIL, '处理失败', ''),
                static::SUCCESS => static::create(static::SUCCESS, '处理成功', ''),
                static::IGNORE => static::create(static::IGNORE, '忽略处理', ''),
                static::UNUSUAL => static::create(static::UNUSUAL, '传入的队列状态异常', '传入的队列状态异常')
            ];
        }
        return static::$logObj;
    }

    /**
     * 查询
     *
     * @param $key
     * @return false|static
     */
    public static function find($key)
    {
        return isset(static::all()[$key]) ? static::all()[$key] : false;
    }

    /**
     * 创建struct
     *
     * @param $key
     * @param $name
     * @param $remark
     * @return $this
     */
    public function create($key, $name, $remark)
    {
        $obj = new static();
        $obj->key = $key;
        $obj->name = $name;
        $obj->remark = $remark;
        return $obj;
    }

}

// 调用代码-原始
public function updateLogStatusById($tenantCode, $logId, $status, $message = '')
    {
        if (!is_scalar($message)) {
            $message = json_encode($message, JSON_UNESCAPED_UNICODE);
        }
        switch ($status) {
            case '消息出列':
                $this->_yfQueueLogService->updateLogStatusById($tenantCode, $logId, ['status' => '消息出列', 'message' => 'yf_queue_plus_iLogId:' . $this->getILogId().'message:' . $message]);
                break;
            case '忽略处理':
                $this->_yfQueueLogService->updateLogStatusById($tenantCode, $logId, ['status' => '忽略处理', 'message' => $message]);
                break;
            case '处理失败':
                $this->_yfQueueLogService->updateLogStatusById($tenantCode, $logId, ['status' => '处理失败', 'message' => $message]);
                break;
            case '处理成功':
                $this->_yfQueueLogService->updateLogStatusById($tenantCode, $logId, ['status' => '处理成功', 'message' => $message]);
                break;
        }
    }

// 调用代码-改造后
public function updateQueueStatue($tenantCode, $logId, $status, $message = '')
    {
        if (!is_scalar($message)) {
            $message = json_encode($message, JSON_UNESCAPED_UNICODE);
        }
        if (!in_array($status, QueueResultLogStatus::comment())) {
            $statusName = QueueResultLogStatus::find($status)->name;
        } else {
            $statusName = QueueResultLogStatus::find(QueueResultLogStatus::UNUSUAL)->name;
        }
        if ($status == QueueResultLogStatus::HANDLING) {
            $message = 'yf_queue_plus_iLogId:' . $this->getILogId().'message:' . $message;
        }
        $updateRow = ['status' => $statusName, 'message' => $message];
        $this->_yfQueueLogService->updateLogStatusById($tenantCode, $logId, $updateRow);
    }

写在最后,留个小作业,下面这段代码请提出你的优化思路:

private function checkClassProblemSheet($sheet, $highestColumn, $highestRow)
    {
        try {
            // 不合法的行
            $failRow = [];

            //返回合法数据
            $legalImportRows = [];

            // 检查项为空的行
            $nullClassRow = [];
            // 检查项名称包含特殊字符的行
            $illegalCharacterRow = [];
            // 检查项不存在的行
            $notExistClassRow = [];
            // 问题描述为空的行
            $nullClassProblemRow = [];
            // 问题描述超过特定字符
            $exceedMaxLengthClassProblemRow = [];
            // 问题描述重复的行
            $classProblemRepeatRow = [];
            // 非末级检查项的行
            $notEndClassRow = [];
            // 装修标准为空的行
            $nullFitmentStandardRow = [];
            // 装修标准不合法的行
            $illegalFitmentStandardRow = [];
            // 装修标准超过检查项的装修标准
            $exceedClassFitmentStandardRow = [];
            // 内验期整改时限为空的行
            $nullInternalRepairRow = [];
            // 内验期整改时限不合法的行
            $illegalInternalRepairRow = [];
            // 交付期整改时限为空的行
            $nullDeliveryRepairRow = [];
            // 交付期整改时限不合法的行
            $illegalDeliveryRepairRow = [];
            // 补充说明是否必填为空的行
            $nullIsRemarkRequiredRow = [];
            // 补充说明是否必填不合法的行
            $illegalIsRemarkRequiredRow = [];

            // 字段到Excel列的转换
            $fieldToColumnMapping = $this->getImportFieldMapping($sheet, $highestColumn, $this->_importClassProblemFields);

            // 提示不存在的列
            $this->checkNotExistColumn($fieldToColumnMapping, $sheet->getTitle());

            for ($row = 3; $row <= $highestRow; $row++) {
                $checkName1 = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['一级检查项名称'], $row)->getValue();
                $checkName1 = !empty($checkName1) ? trim($checkName1) : '';

                $checkName2 = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['二级检查项名称'], $row)->getValue();
                $checkName2 = !empty($checkName2) ? trim($checkName2) : '';

                $checkName3 = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['三级检查项名称'], $row)->getValue();
                $checkName3 = !empty($checkName3) ? trim($checkName3) : '';

                $checkName4 = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['四级检查项名称'], $row)->getValue();
                $checkName4 = !empty($checkName4) ? trim($checkName4) : '';

                $checkName5 = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['五级检查项名称'], $row)->getValue();
                $checkName5 = !empty($checkName5) ? trim($checkName5) : '';

                $classProblem = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['问题描述'], $row)->getValue();
                $classProblem = !empty($classProblem) ? trim($classProblem) : '';

                $fitRange = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['适用装修标准'], $row)->getValue();
                $internalRepairDeadline = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['内验期整改时限'], $row)->getValue();
                $deliveryRepairDeadline = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['交付期整改时限'], $row)->getValue();
                $isRemarkRequired = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['补充说明是否必填'], $row)->getValue();

                if (empty($checkName1) && empty($checkName2) && empty($checkName3) && empty($checkName4) && empty($checkName5) && empty($classProblem) && empty($fitRange) && empty($internalRepairDeadline) && empty($deliveryRepairDeadline) && empty($isRemarkRequired)) {
                    // 忽略空行
                    continue;
                }

                if ((empty($checkName1) && empty($checkName2) && empty($checkName3) && empty($checkName4) && empty($checkName5))
                    || ((!empty($checkName2) && empty($checkName1)) || (!empty($checkName3) && empty($checkName2)) || (!empty($checkName4) && empty($checkName3)) || (!empty($checkName5) && empty($checkName4)))) {
                    // 检查项为空(包含该级检查项不为空但是上级检查项为空的情况)
                    $nullClassRow[] = $row;
                }

                if (strpos($checkName1, '-') !== false || strpos($checkName2, '-') !== false || strpos($checkName3, '-') !== false || strpos($checkName4, '-') !== false || strpos($checkName5, '-') !== false) {
                    // 检查项名称有特殊字符
                    $illegalCharacterRow[] = $row;
                }

                $strCheckName = $checkName1;
                if (!empty($checkName2)) {
                    $strCheckName .= '-' . $checkName2;
                }
                if (!empty($checkName3)) {
                    $strCheckName .= '-' . $checkName3;
                }
                if (!empty($checkName4)) {
                    $strCheckName .= '-' . $checkName4;
                }
                if (!empty($checkName5)) {
                    $strCheckName .= '-' . $checkName5;
                }
                if (!empty($strCheckName) && !in_array($strCheckName, $this->_checkName) && !in_array($row, $illegalCharacterRow)) {
                    // 问题描述中的检查项不存在
                    $notExistClassRow[] = $row;
                }

                if (!empty($strCheckName) && $this->isNotEndClass($strCheckName)) {
                    // 问题描述对应的检查项非末级检查项
                    $notEndClassRow[] = $row;
                }

                if (empty($classProblem)) {
                    // 问题描述为空
                    $nullClassProblemRow[] = $row;
                }

                if (!empty($classProblem) && mb_strlen($classProblem, 'utf-8') > 30) {
                    // 问题描述长度超过特定字符
                    $exceedMaxLengthClassProblemRow[] = $row;
                }

                $importData = [
                    'check_name1' => $checkName1,
                    'check_name2' => $checkName2,
                    'check_name3' => $checkName3,
                    'check_name4' => $checkName4,
                    'check_name5' => $checkName5,
                    'class_problem' => $classProblem
                ];

                $repeatRow = $this->getRepeatClassProblemRow($legalImportRows, $importData);
                if ($repeatRow) {
                    if (!in_array($repeatRow, $classProblemRepeatRow)) {
                        // 记录重复的行数
                        $classProblemRepeatRow[] = $repeatRow;
                    }

                    // 问题描述重复
                    $classProblemRepeatRow[] = $row;
                }

                if (empty($fitRange)) {
                    // 适用装修标准为空
                    $nullFitmentStandardRow[] = $row;
                }

                if (!empty($fitRange) && !in_array($fitRange, ['毛坯', '精装', '毛坯&精装'])) {
                    // 装修标准不合法
                    $illegalFitmentStandardRow[] = $row;
                }

                if (!empty($fitRange) && !empty($this->_classRange[$strCheckName])) {
                    // 问题描述的适用装修标准不超过检查项的适用装修标准
                    $checkItemFitRange = $this->_classRange[$strCheckName];
                    if (($checkItemFitRange == '精装' && strpos($fitRange, '毛坯') !== false) || ($checkItemFitRange == '毛坯' && strpos($fitRange, '精装') !== false)) {
                        $exceedClassFitmentStandardRow[] = $row;
                    }
                }

                if ($internalRepairDeadline == '') {
                    // 内验整改期限为空
                    $nullInternalRepairRow[] = $row;
                } elseif (!preg_match('/^[1-9][0-9]*$/', $internalRepairDeadline)) {
                    // 内验整改期限不合法
                    $illegalInternalRepairRow[] = $row;
                }

                if ($deliveryRepairDeadline == '') {
                    // 交付整改期限为空
                    $nullDeliveryRepairRow[] = $row;
                } elseif (!preg_match('/^[1-9][0-9]*$/', $deliveryRepairDeadline)) {
                    // 交付整改期限不合法
                    $illegalDeliveryRepairRow[] = $row;
                }

                if (empty($isRemarkRequired)) {
                    // 补充说明是否必填为空
                    $nullIsRemarkRequiredRow[] = $row;
                }

                if (!empty($isRemarkRequired) && !in_array($isRemarkRequired, ['是', '否'])) {
                    // 补充说明是否必填不合法
                    $illegalIsRemarkRequiredRow[] = $row;
                }

                $legalImportRows[$row] = [
                    'check_name1' => $checkName1,
                    'check_name2' => $checkName2,
                    'check_name3' => $checkName3,
                    'check_name4' => $checkName4,
                    'check_name5' => $checkName5,
                    'full_name' => $strCheckName,
                    'class_problem' => $classProblem,
                    'is_for_rough' => strpos($fitRange, '毛坯') !== false ? 1 : 0,
                    'is_for_decorated' => strpos($fitRange, '精装') !== false ? 1 : 0,
                    'internal_repair_deadline' => $internalRepairDeadline,
                    'delivery_repair_deadline' => $deliveryRepairDeadline,
                    'is_remark_required' => $isRemarkRequired == '是' ? 1 : 0
                ];
            }

            if (!empty($nullClassRow)) {
                $failRow['检查项为空'] = $nullClassRow;
            }

            if (!empty($illegalCharacterRow)) {
                $failRow['检查项名称包含非法字符'] = $illegalCharacterRow;
            }

            if (!empty($notExistClassRow)) {
                $failRow['检查项不存在'] = $notExistClassRow;
            }

            if (!empty($notEndClassRow)) {
                $failRow['非末级检查项不能添加问题描述'] = $notEndClassRow;
            }

            if (!empty($nullClassProblemRow)) {
                $failRow['问题描述为空'] = $nullClassProblemRow;
            }

            if (!empty($exceedMaxLengthClassProblemRow)) {
                $failRow['问题描述超过30个字'] = $exceedMaxLengthClassProblemRow;
            }

            if (!empty($classProblemRepeatRow)) {
                $failRow['检查项+问题描述重名'] = $classProblemRepeatRow;
            }

            if (!empty($nullFitmentStandardRow)) {
                $failRow['适用装修标准为空'] = $nullFitmentStandardRow;
            }

            if (!empty($illegalFitmentStandardRow)) {
                $failRow['适用装修标准不合法'] = $illegalFitmentStandardRow;
            }

            if (!empty($exceedClassFitmentStandardRow)) {
                $failRow['问题描述的适用装修标准与检查项的不匹配'] = $exceedClassFitmentStandardRow;
            }

            if (!empty($nullInternalRepairRow)) {
                $failRow['内验期整改时限为空'] = $nullInternalRepairRow;
            }

            if (!empty($illegalInternalRepairRow)) {
                $failRow['内验期整改时限不合法'] = $illegalInternalRepairRow;
            }

            if (!empty($nullDeliveryRepairRow)) {
                $failRow['交付期整改时限为空'] = $nullDeliveryRepairRow;
            }

            if (!empty($illegalDeliveryRepairRow)) {
                $failRow['交付期整改时限不合法'] = $illegalDeliveryRepairRow;
            }

            if (!empty($nullIsRemarkRequiredRow)) {
                $failRow['补充说明是否必填为空'] = $nullIsRemarkRequiredRow;
            }

            if (!empty($illegalIsRemarkRequiredRow)) {
                $failRow['补充说明是否必填不合法'] = $illegalIsRemarkRequiredRow;
            }

            return ['failRow' => $failRow, 'legalImportRows' => $legalImportRows];
        } catch (InfoException $iex) {
            throw $iex;
        }
    }

你可能感兴趣的:(编程范式全貌概要-免费)