我可以将 empty() 用于未定义的变量吗?

Can i use empty() for a Undefined Variable?

index.php:

if(isset($_SESSION['user_id'])){
  $user_id = $_SESSION['user_id'];
}
###$_SESSION['user_id'] is = to $user['user_id'] that i get from database in a select query on login.php

正如您在上面看到的,我仅在用户登录时才设置变量 $user_id。我使用 if(empty($user_id)){} 来设置一些条件并且它运行良好,但我很好奇,因为如果用户未登录,$user_id 将是一个未定义的变量,我是 php 的新手,所以我想知道我是否做对了。

在用户登录后不应查看的页面上,我添加了以下代码:

if(empty($user_id)) {
  $_SESSION['message'] = "You need to loge in to view this page";
  header("location: verify/error.php");
  exit;    
}

并显示一些与用户相关的内容,我使用此代码:

if(!empty($user_id)){
   echo 'content related to the user, divs...etc';
}

我有两个问题:

1 - 我做的对吗?

2 - 我应该将代码更改为:

if(isset($_SESSION['user_id'])){
  $user_id = $_SESSION['user_id'];
}else{
  $user_id = '';
}

要检查是否已设置变量,您应该使用 isset() 来测试它。 empty() 也将 return true 如果 returned 为假值。在文档页面中,以下值被认为是错误的:

"" (an empty string)

0 (0 as an integer)

0.0 (0 as a float)

"0" (0 as a string)

NULL

FALSE

array() (an empty array)

因此,如果 $user_id 可能具有这些值之一(可能是整数 0),那么您可能会得到意想不到的结果。如果 $user_id 永远不会成为这些值之一,那么您可以检查它是否使用 !empty() 设置而不会出现任何问题。

Am I doing it correctly?

首先,我的回答不会描述在您的案例中使用 empty() 来应对未定义变量警告的实现。这个答案是为了给你方向,并向你展示一些适合你的情况的 OOP 以及为什么它有效。

举个例子,页面 A 不需要用户进行读取访问身份验证,但页面 B 需要。

我们可以编写一个 class 结构,根据访问范围为您执行此操作,而不是在整个页面 'n' 的应用程序中始终如一地重新编写此检查。

让我们首先通过创建接口让应用程序知道何时禁止读取访问。每当我们想在用户未登录时撤销读取访问权限时,这最终将用于扩展用户身份验证class。

namespace Application\Auth;

interface MustBeLoggedIn
{
}

接下来,我们可以继续构建身份验证 class。这就是这个 class 将根据其实例类型执行其方法的方式或顺序的指令。

namespace Application\Auth;

class Authenticable {

    protected $_user_id;
    private $_csrf = 'user_id';

    public function __construct()
    {
        # PHP 7+
        $this->_user_id = $_SESSION[$this->_csrf] ?? '';

        # PHP < 5.6
        # $this->user_id = isset($_SESSION[$this->_csrf]) ? $_SESSION[$this->csrf] : '';

        if($this instanceof MustBeLoggedIn)
            $this->mustBeLoggedIn();
    }

    private function mustBeLoggedIn()
    {
        if(!isset($this->_user_id))
            $this->authError();
    }

    protected function authError()
    {
        exit();
    }

    protected function isLoggedIn()
    {
        return isset($this->_user_id);
    }

}

最后,我们可以建立两个用户 classes。第一个 class 将撤销任何未授权用户的读取权限。

namespace Application\Auth;

class User extends Authenticable implements MustBeLoggedIn
{
    public function doSomething()
    {
        if($this->isLoggedIn())
            echo $this->_user_id;
    }

    protected function authError()
    {
        $_SESSION['message'] = 'Oh snap! Looks like you need to be logged in to view this page.';
        header('Location: verify/error');
        exit();
    }
}

第二个 class 将允许来宾和经过身份验证的用户具有读取权限。

namespace Application;

class User extends Authenticable
{
    public function doSomething()
    {
        if($this->isLoggedIn())
            echo $this->_user_id;

        if(!$this->isLoggedIn())
            echo 'Well, you can still view this page';
    }
}

部署视图现在很容易。无需不断地重新声明该值是否存在,并决定要做什么,您可以实例化适合您范围的任何一个。

如果我们想在用户登录或注销时给予读取权限,我们可以使用Application\User.

class MyView extends \Application\User {
    public function sayHi() {
        echo $this->isLoggedIn() ? "Hi, {$this->user_id}!" : "Hi, Guest!";
    }
}

(new MyView())->sayHi();

如果我们想撤销未授权用户的读取权限,我们可以使用Application\Auth\User。

class MyView extends \Application\Auth\User {
    public function sayHi() {
        echo "Hi, {$this->user_id}!";
    }
}

(new MyView())->sayHi(); # Guests will be redirected to error/verify

我希望通过向您展示此内容,它可以帮助您更多地了解 PHP。此外,需要注意的是,您可能容易受到 CSRF attacks because an ID (assuming representing an integer) is extremely easy to forge. Perhaps look at using a JSON Web Token 的攻击并将用户 ID 传递给它。

$_SESSION['csrf'] = \Firebase\JWT\JWT::encode(array('user_id' => 1), 'secret'); # An integer represented a base-64 encoding is going to stand out, perhaps store a generated unique token or something else in regards to identifying this user
$user = \Firebase\JWT\JWT::decode($_SESSION['csrf'], 'secret', array('HS256')); # $user['user_id'] would hold 1

关于 fyrye 关于 JWT 使用中的利用的评论,最重要的一点是 HS256 安全性主要来自秘密。 这是什么意思?这意味着您可以控制自己的安全程度。我用的方法是想一个安全的密码,但是跟JWT有点关系。

$secret = '_userAuthentication%App';

然后,为了提高它的安全性,我在将密码提供给 HS256 进行加密之前对密码进行哈希处理。

use \Firebase\JWT\JWT;

JWT::encode(array(
    'Data' => 'You want to secure from the user',
    'Integrity' => 'This STILL should not mean it is trusted data'
), password_hash($secret, PASSWORD_BCRYPT));

解码 JWT 时,记住基础知识也很重要 - 永远不要相信数据。我们可以利用 try catch finally 块,在这种情况下忽略 finally,以确保我们得到正确的信息。

use \Firebase\JWT\JWT;

try {
    $csrf = JWT::decode($jwt, password_hash($secret, PASSWORD_BCRYPT), array('HS256'));
} catch (Exception $e) {
    // Data was invalid - Potential forge ?
}

最后请注意,如果您使用的是 SSL - 现在这是一项要求,请使用 RSA 签署 JWT。