• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Rzuci ktoś okiem na prostą aplikację PHP?

Object Storage Arubacloud
0 głosów
164 wizyt
pytanie zadane 30 stycznia w PHP przez whiteman808 Obywatel (1,820 p.)

Hej, napisałem taką prostą witrynę w PHP, będę wdzięczny za code review :-)

lib/app_init.php

<?php
declare(strict_types=1);

require_once 'config/db.php';

try {
    $db_conn = new PDO(PDO_DSN, DB_USER, DB_PASS);

    $db_conn->query('ALTER TABLE IF EXISTS author DROP FOREIGN KEY fk_author_role_id');
    $db_conn->query('ALTER TABLE IF EXISTS post DROP FOREIGN KEY fk_post_author_id');
    foreach (['author_role', 'author', 'post'] as $db_table) {
        $db_conn->query("DROP TABLE IF EXISTS $db_table");
    }
    $stmt = 'CREATE TABLE IF NOT EXISTS author_role (' .
        'id int NOT NULL PRIMARY KEY AUTO_INCREMENT, ' .
        'role_name varchar(100) UNIQUE NOT NULL)';
    $db_conn->query($stmt);
    $stmt = 'INSERT INTO author_role (role_name) VALUES ' .
        "('Administrator'), ('Moderator'), ('User')";
    $db_conn->query($stmt);
    $stmt = 'CREATE TABLE IF NOT EXISTS author (' .
        'id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, ' .
        'username varchar(255) UNIQUE NOT NULL, ' .
        'passwd varchar(255) NOT NULL, ' .
        'email varchar(255) UNIQUE NOT NULL, ' .
        'created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, ' .
        'role_id int(11) NOT NULL, ' .
        'FOREIGN KEY fk_author_role_id (role_id) REFERENCES author_role (id))';
    $db_conn->query($stmt);
    $stmt = 'INSERT INTO author (username, passwd, email, role_id) ' .
        "VALUES ('lester29', 'test', 'lester29@lester29.org', 1)";
    $db_conn->query($stmt);
    $stmt = 'CREATE TABLE IF NOT EXISTS post ( ' .
        'id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, ' .
        'title varchar(255) UNIQUE NOT NULL, ' .
        'content text NOT NULL, ' .
        'author_id int(11) NOT NULL, ' .
        'created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, ' .
        'updated_at DATETIME NOT NULL ON UPDATE CURRENT_TIMESTAMP, ' .
        'FOREIGN KEY fk_post_author_id (author_id) REFERENCES author (id))';
    $db_conn->query($stmt);

    echo 'Successfully created required tables.';
} catch (PDOException $ex) {
    echo "An error occurred while initializing database: " . $ex->getMessage();
}

lib/user.php

<?php
declare(strict_types=1);

require_once 'config/db.php';

function userExists(string $username, string $password): bool
{
    try {
        $db_conn = new PDO(PDO_DSN, DB_USER, DB_PASS);
        $stmt = 'SELECT username, passwd ' .
            'FROM author ' .
            "WHERE username = ? " .
            "AND passwd = ?";
        $query = $db_conn->prepare($stmt);
        $query->execute([$username, $password]);
        return $query->rowCount() > 0;
    } catch (PDOException $ex) {
        die('An error occurred at auth_user function: ' . $ex->getMessage());
    }
}

footer.php

<footer>
    <p>This website is owned by lester29</p>
</footer>
</body>
</html>

header.php

<?php
session_start();

if (!isset($_SESSION['auth_user'])) {
    $_SESSION['auth_user'] = false;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>lester29</title>
</head>
<body>
    <header>
        <h1>lester29</h1>
        <p>My first website :-)</p>
        <?php if ($_SESSION['auth_user']): ?>
            <p>You're logged as <?php echo $_SESSION['username']; ?>.</p>
        <?php endif; ?>
    </header>
    <section>
        <nav>
            <ul>
                <li><a href="index.php">Home</a></li>
                <?php if ($_SESSION['auth_user']): ?>
                    <li><a href="transact_user.php?action=logout">Log out</a></li>
                <?php else: ?>
                    <li><a href="login.php">Login</a></li>
                <?php endif; ?>
            </ul>
        </nav>
    </section>
    <main>

index.php

<?php
require_once 'lib/user.php';
require_once 'header.php';
?>
    <section>
        <h2>Posts</h2>
        <?php
        try {
            $db_conn = new PDO(PDO_DSN, DB_USER, DB_PASS);
            $stmt = 'SELECT post.title AS post_title, ' .
                'author.username AS post_author, ' .
                'post.created_at AS post_created, ' .
                'post.content AS post_content ' .
                'FROM post ' .
                'LEFT JOIN author ' .
                'ON post.author_id = author.id';
            /** @var PDOStatement $posts */
            $posts = $db_conn->query($stmt);
            while ($post = $posts->fetch(PDO::FETCH_ASSOC)) {
                ?>
                <article>
                    <h3><?php echo $post['post_title']; ?></h3>
                    <p>Created at <?php echo $post['post_author']; ?> by <?php echo $post['$post_author']; ?></p>
                    <p>
                        <?php echo htmlspecialchars(nl2br($post['post_content'])); ?>
                    </p>
                </article>
                <?php
            }

            if ($posts->rowCount() == 0) {
                echo '<p>No such any posts.</p>';
            }
        } catch (PDOException $ex) {
            echo '<p>An error occurred during retrieving posts: ';
            echo $ex->getMessage();
            echo '</p>';
        }
        ?>
    </section>
<?php
require_once 'footer.php';

login.php

<?php
require_once 'header.php';
?>
    <section>
        <?php if ($_SESSION['auth_user']): ?>
            <h2>Information</h2>
            <p>
                It seems like you're already logged in.
                Do you want to <a href="transact_user.php?action=logout">log out</a>?
            </p>
        <?php else: ?>
            <h2>Sign in</h2>
            <form action="transact_user.php?action=login" method="post">
                <p>
                    <label for="username">Username</label>
                    <input type="text" name="username" id="username">
                </p>
                <p>
                    <label for="password">Password</label>
                    <input type="password" name="password" id="password">
                </p>
                <p>
                    <input type="submit" name="submit_login" value="Login">
                </p>
            </form>
        <?php endif; ?>
    </section>
<?php
require_once 'footer.php';

transact_user.php

<?php
require_once 'header.php';
require_once 'config/db.php';
require_once 'lib/user.php';
?>
    <section>
        <?php
        if (!isset($_GET['action'])) {
            ?>
            <h2>Error</h2>
            <p>No action chosen for user.</p>
            <?php
        } else {
            switch ($_GET['action']) {
                case 'login':
                    if (isset($_POST['submit_login'])) {
                        $username = $_POST['username'];
                        $password = $_POST['password'];
                        if (userExists($username, $password)) {
                            $_SESSION['username'] = $username;
                            $_SESSION['auth_user'] = true;
                            ?>
                            <h2>Success</h2>
                            <p>You're logged in.</p>
                            <?php
                        } else {
                            $_SESSION['auth_user'] = false;
                            ?>
                            <h2>Login failed</h2>
                            <p>Incorrect login or password. <a href="login.php">Try again</a></p>
                            <?php
                        }
                    }
                    break;
                case 'logout':
                    if ($_SESSION['auth_user']) {
                        $_SESSION['auth_user'] = false;
                        ?>
                        <h2>Success</h2>
                        <p>You're successfully log out!</p>
                        <?php
                    } else {
                        ?>
                        <h2>Information</h2>
                        <p>
                            It seems you're not logged in. Do you want to
                            <a href="login.php">log in</a> again?
                        </p>
                        <?php
                    }
                    break;
                default:
                    ?>
                    <h2>Error</h2>
                    <p>Invalid action <code><?php echo $_GET['action']; ?></code>.</p>
                <?php
            }
        }
        ?>
    </section>
<?php
require_once 'footer.php';

 

komentarz 30 stycznia przez Velta Maniak (52,370 p.)

Jak dla mnie:

  • W przypadku wielu zapytań bez parametrów, preferowałbym utworzenie tablicy z ich treścią i wywołanie podczas iteracji – a przynajmniej dla mnie byłoby to czytelniejsze.
  • Choć prawdopodobnie piszesz aplikację (na razie) na użytek własny, należy pamiętać, że wszelkie produkcyjne systemy implementujące strukturę przechowania informacji o użytkownikach powinny (wręcz: muszą, ze względu na obowiązujące prawo) jednostronnie ukrywać ich hasła, czego można dokonać operacją hashowania – warto o tym wiedzieć i się tym zainteresować.
    • Wypróbuj algorytm bcrypt – został wystarczająco dobrze przetestowany na przestrzeni lat. W PHP można utworzyć wartość funkcji skrótu przez funkcję password_hash, a domyślnie używanym algorytmem jest właśnie bcrypt.
    • W przypadku ukrywanych haseł, porównujesz ze sobą wersje utajnione, zamiast jawnych – w PHP odpowiada za to funkcja password_verify.
  • Nazwy tabel powinny być określone w liczbie mnogiej. Przechowujesz nie rolę (author_role), tylko wiele ról (authors_roles; swoją drogą plus za utworzenie tej tablicy pomocniczej, zapewniającej właściwą integralność), nie autora (author), a autorów (authors) – i tak dalej.
  • Dla logiki sprawdzania istnienia użytkownika w bazie, warto użyć operatora podzapytania EXISTS, ponieważ takie zapytanie jest w stanie zatrzymać się w przypadku odnalezienia pożądanego rekordu, a COUNT przeszuka tak czy tak całą tabelę, pogorszając wydajność tej operacji.
    EXISTS zwróci całkowitoliczbowy odpowiednik wartości logicznej.
$stmt = 'SELECT EXISTS(SELECT *' .
    'FROM author ' .
    "WHERE username = ? " .
    "AND passwd = ?)";
$query = $db_conn->prepare($stmt);
$query->execute([$username, $password]);
return boolval($query->fetchColumn());

 

komentarz 31 stycznia przez Ehlert Ekspert (212,670 p.)
  •  - jest też argon

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
4 odpowiedzi 448 wizyt
pytanie zadane 23 października 2018 w Java przez niezalogowany
0 głosów
1 odpowiedź 194 wizyt
pytanie zadane 3 marca 2018 w PHP przez fipooo Bywalec (2,880 p.)
–1 głos
1 odpowiedź 154 wizyt
pytanie zadane 8 stycznia 2020 w C i C++ przez Nabuchadonozor Gaduła (3,120 p.)

92,580 zapytań

141,432 odpowiedzi

319,664 komentarzy

61,965 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...