【问题标题】:Procedural PHP to where?PHP 程序到哪里去了?
【发布时间】:2012-09-23 19:38:09
【问题描述】:

我项目中的所有代码都是程序性的。它没有使用任何框架或任何 MVC 结构。大多数 PHP 是带有一些逻辑的表单处理程序。很多这样的代码:

<?php

require "headerFile.php";

$user = array(); // filled with user data

if (isset($_POST['submitButton'])) {
    if (isset($_POST['money'])) {
        if (is_numeric($_POST['money'])) { // I don't actually validate numbers this way
            if ($_POST['money'] <= $user['money']) {
                $query = mysql_query("SELECT * FROM someTable WHERE id={$user['id']}");

                if($result = mysql_fetch_array($query)) {
                    if ($someOtherCheck == $user['someOtherData']) {
                        /*
                         * run some queries, give user some item
                         *
                         */
                    } else {
                        echo "This isn't right.";
                    }
                } else {
                    echo "You don't have a row in some table!";
                }
            } else {
                echo "You don't have that much money!";
            }
        } else {
            echo "Enter a valid number";
        }
    } else {
        echo "Enter how much you want to wager.";
    }
}

// some hard coded form goes here

require "footerFile.php";

?>

有超过一百种这样的表单和嵌套的 if 处理程序,其中一些与更多/更少的条件相似。

我想开始使用 OOP,我一直在阅读设计模式,但似乎找不到任何适用于我的项目的东西。

我正在添加类似于上述代码块的大量新代码块,我不想复制和粘贴,只是在这里和那里更改一些位,我有什么选择?

【问题讨论】:

标签: php oop design-patterns frameworks


【解决方案1】:

首先,如果您曾经复制和粘贴应该是 BIG RED FLAG 的代码。这应该是一个警告,您需要编写一个可以使用的通用函数,而不是复制和粘贴。

其次,是时候停止使用类似mysql_query 的函数了。甚至PHP page 也表示不鼓励使用这些功能。我将开始将您的 SQL 转移到更像 OO 的 PDO。

当我继承了一个看起来像您发布的代码的应用程序时,我写了一篇关于我开始处理事情所采取的步骤的博客文章。它也可能对你有帮助 - http://mattmccormick.ca/2011/10/13/how-to-refactor-a-php-application/

【讨论】:

    【解决方案2】:

    OOP 并不总是与应用程序有关。它是关于可读性和结构的。老实说,您在此处发布的代码的可读性如何?有很多事情正在发生,你需要 10 分钟才能把它们全部破译。但是,如果您将其分解为具有类函数的类,您将能够浏览事物并知道发生了什么。

    OOP 并不总是为 php 做很多事情,但它是您想要为几乎所有其他非静态加载语言做的事情。另一个好处是,如果您在一个项目中拥有多个程序员,那么您都可以阅读代码。评论是你的朋友。

    最好的选择是使用带返回的函数。返回该值,然后该函数执行它需要做的任何事情。我个人会做类似的事情

    $class->检查

    如果出错

    $this->error_handler

    并且函数错误处理程序执行您希望它对错误执行的任何操作,如果它的死亡或回显然后执行此操作,但是您构建错误处理程序,因此如果您想要更改它,您可以在一个地方进行而不是 20。

    【讨论】:

    • 我想要处理更多错误,因此错误处理程序类是个好主意。而且我会相当快地建立一个检查库,这将缩短和提高这些代码块的可读性:) 但我仍然会复制和粘贴并进行轻微的调整,但会添加一个全新的块。 ://
    【解决方案3】:

    即使不引入 OOP,您也可以为构建可读的代码创造奇迹。如果它们不相互依赖,则无需嵌套条件。

    $form_is_valid = true;
    
    if (!isset($_POST['submitButton'])) {
        echo "This isn't right.";
        $form_is_valid = false;
    }
    
    if (!isset($_POST['money'])) {
        echo "Enter how much you want to wager.";
        $form_is_valid = false;
    } elseif (!is_numeric($_POST['money'])) {
        echo "Enter a valid number";
        $form_is_valid = false;
    } elseif (!$_POST['money'] <= $user['money']) {
        echo "You don't have that much money!";
        $form_is_valid = false;
    }
    
    if ($form_is_valid) {
        do_stuff_here();
    }
    

    【讨论】:

    • +1 使条件和相关代码块彼此相邻,这很好
    【解决方案4】:

    关于框架

    一个体面的框架将帮助您通过关注点分离更好地组织代码,但不一定强制执行最佳实践。主观上,我认为在您将最佳实践根深蒂固之前,需要实践经验并犯很多错误。

    尝试将框架仅视为一种交付机制。理想情况下,您的代码不依赖于任何一个框架。这通常意味着使用组件来处理应用程序的不同方面,例如路由、验证、配置、事件、依赖项等。


    关于 OOP

    我觉得你应该从SOLID principle开始。这将帮助你,虽然不能保证,但避免犯错误,这会花费你大量的时间。

    最重要的是,您的对象应该只有一个职责。例如Product 对象不应负责充当数据存储、持久化自身、处理订单等。

    另外一个大问题是依赖注入。这对于单元测试你的类来说是巨大的(你应该养成这样做的习惯)。简而言之,不要在一个类构造依赖。预先构造它并通过构造函数参数或 setter 方法传入。

    构建应用程序的过程值得拥有自己的书籍,所以我不打算在这里尝试全部编写。但是,如果您遵循 SOLID 原则,那么您将在成为更好的开发人员的道路上取得成功。


    关于您上面的代码

    那种嵌套一般是一种气味,我明白你为什么要问这个问题......

    第 1 步是将验证分离到它自己的服务中。至少在 MVC 意义上考虑这一点,您的控制器将只有 [pseudocode] if ($form-&gt;isValid()) { do something }。仅此一项就消除了您拥有的大部分意大利面。

    【讨论】:

      【解决方案5】:

      我想开始使用 OOP,我一直在阅读设计模式,但似乎找不到任何适用于我的项目的东西。

      您不必从patterns 开始.. 了解基础知识,您可以在那里进步..

      这是一个基本的例子

      if (isset($_POST['submitButton'])) {
      
          $db = new \mysqli("localhost", "user", "pass", "db");
      
          $user = new User("Juddling Stack", 123456);
          $user->setMoney(500.01);
      
          $player = new PlayerRegister($db, $user);
      
          if (! isset($_POST['money']) || ! is_numeric($_POST['money']))
              $player->addError("Enter Valid Wager Money");
      
          if ($_POST['money'] <= $user['money']) {
              $player->addError("You don't have that much money!");
          }
      
          try {
              $player->doSQL();
          } catch ( BaseException $e ) {
      
              foreach ( $e->getError() as $error ) {
                  echo $error, " <br />";
              }
          }
      }
      

      Classes Used

      【讨论】:

        【解决方案6】:

        看起来您的代码正在组合模型层和视图层的某些组件。您正在对数据库运行查询,并且在同一个地方,您正在包含硬编码的表单。因此,一个不错的起点是将这些任务分成两个单独的类。例如,编写一个类来连接到您的数据库并对其运行查询,并编写另一个类来实际提供您的内容。

        我对设计模式的建议是不要太拘泥于细节。相反,请尝试了解为什么某些模式如此有用,以及他们试图解决的什么问题。许多初学者在如何中陷入了困境,最终浪费了很多时间来学习框架的细微差别,而一个简单的解决方案就足够了。

        最后,当您阅读代码时,请留意可能构成类的内容。另外,请记住专业化是关键,通过构建非常专业化的类,您正在构建可能用于其他项目的模块化组件,

        【讨论】:

          【解决方案7】:

          让我们首先说明您遇到的问题,它不是使用 OOP,而是称为 programming-overhead 或程序员术语 spaghetti-code

          如果您遇到很多开销,这意味着浪费时间编写几乎完全相同的代码行,其中只有内容不同,但功能相同。然后开始切分与其功能相同但内容不同的代码。

          您说还有更多要复制粘贴,甚至更复杂,我将只执行 form 验证部分(我称之为阶段 1),这只是如何应用逻辑的一个简单示例通过提供预期的输入为您完成所有工作。一个例子可能比其他例子更优雅。

          以下所有代码均未经测试

          定位具有相同功能的代码的示例。

           // functionality on checking values is the same, but it's content is not
           if (isset($_POST['submitButton'])) {
             if (isset($_POST['money'])) {
               if (is_numeric($_POST['money'])) {
          

           // though every decision made by its content is only being produced once .. 
                } else
                    echo "You don't have that much money!";
              } else
                  echo "Enter a valid number";
          } else
              echo "Enter how much you want to wager.";
          

          现在的诀窍是找到一个从逻辑上解决这个问题的解决方案。 PHP的内置函数很多,但首先要掌握如何解决它的想法。一个示例是让每个key,如submitButtonmoney,如果没有设置/存在,则其值等于not exists,假设null。其次,您将值与$_POST 提供的键进行比较,所以无论如何......您的$_POST 数组是决策者。

          可以看到一个很好的例子,jQuery 库是如何使用 $.extend() 方法构建的如果存在。但是 PHP 也可以。

          让我们默认值。

          $_POST = array_merge(array(
            'submitButton' => null,
            'money' => 0,
            'etc' => '...'
          , $_POST);
          

          现在构建一个函数来验证这个数组要容易得多,因为您总是可以依赖一个存在的值。

          您说您有更多需要验证的表单,接下来要解决的是验证某些字段的函数。

          validinvalid 形式的正式表示可以是一个数组,例如

          $valid_form = array(
            'submitButton' => array('not_null'),
            'money' => array('not_null','int'),
            'etc' => '...'
          ); 
          

          要验证的函数是

          function validateForm($values, $valid) {
            // error array to be returned
            $error = array();
            // let's iterate over each value, remember we defaulted the $_POST array with
            // all the fields it can have, so all fields should be iterated apon.
            foreach($values as $key => $value) {
              if(array_key_exist($key, $valid)) {
                // logic content can be used by different functions, switch
                // used here for simplicity
                foreach($valid[$key] as $validation) {
                  switch($validation) {
                    case 'not_null':
                      if(is_null($value)) {
                        $error[] = "error logic";
                        continue; // skip rest
                      }
                    break;
                    case 'etc':
                      $error[] = "..";
                    break;
                  }
                }
              }
            }
            return $error ? $error : true; // true being valid
          }
          

          错误处理可以通过多种方式完成,仅举一个简单的例子(取决于这个项目的广泛程度),您可以将错误内容绑定到验证键,例如

          $vfe = $valid_form_errors = array( // $vfe for simlicity's sake
            '__no_error' => 'no error present for "%key%" validation',
            'not_null' => '%key% should not be null',
            'int' => '%key% expects to be an integer'
          );
          
          $valid_form = array(
            'submitButton' => array('not_null'),
            'money' => array('not_null','int'),
            'etc' => '...'
          );
          

          创建正式错误消息的函数

          function error_msg($key, $validation) {
            global $vfe;
            // error exists?
            $eE = array_key_exists($validation,$vfe);
            return str_replace('%key%', $eE?$key:$validation, $vfe[$eE?$validation:'__no_error']);
          }
          

          而在简单的开关中,错误逻辑是

            foreach($valid[$key] as $validation) {
              switch($validation) {
                case 'not_null':
                  if(is_null($value))
                    $error[] = error_msg($key, $validation);
                break;
                case 'etc':
                  $error[] = "..";
                break;
              }
            }
          

          那么你的代码使用不同的逻辑会是什么样子?

              // first stage ..
              $form_valid = validateForm($_POST, $valid_form);    
              if ($form_valid === true) {
                // second stage, same logic be applied as with validateForm, etc.
                if($_POST['money'] <= $user['money']) {
                  $query = mysql_query("SELECT * FROM someTable WHERE id={$user['id']}");
          
                  if($result = mysql_fetch_array($query)) {
                      // third stage, same logic can be applied here..
                      if ($someOtherCheck == $user['someOtherData']) {
          
                      } else {
                          echo "This isn't right.";
                      }
                  } else {
                      echo "You don't have a row in some table!";
                  }
              }
              else {
                $errors = $form_valid;
                // error handling here
                print_r($errors);
              }    
          

          这完全取决于您可以定义预期值的具体程度。您可以将每个功能扩展为更具体,例如将错误绑定到表单键,以便您可以在稍后阶段专门针对该输入。他们的关键是消除所有可能的重复,其中一个函数可能会为您完成所有这些,只需让他比较您期望的值,并让他告诉他实际存在哪些值。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-07-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-06-19
            • 1970-01-01
            相关资源
            最近更新 更多