如何做一个网站?

· · 科技·工程

前言

大家好,又来挖坑了,前段时间做了一个网站,网站主要是一个休闲娱乐为主题的,但平时感觉不常用、而且比较简单且垃圾

接下来我就给大家介绍一下我的网站的制作过程(全是免费的,没有花一分钱),还有一些坑给大家避一下。

一样一样,还是 Windows,别的用不了这个方法。

准备工作

Html or php

::::info[一大堆废话]

首先我们先把他们的全称写一下:

通过全称,我们可以看到:HTML 其实就是将你的代码放在网页上;php 你可以理解为就是把你的代码预处理成 html,然后再运行 html 在网页上。

如果你只是想做一个静态的,那就用 html,因为 html 在本地电脑上就可以运行。

但你想做一个网站比如可以登录的,那就用 php,但是 php 有一个缺点就是你需要下载一些可以运行 php 的东西。 ::::

一句话概括上面的废话,建议用 php。

链接:https://www.php.net/downloads.php 我就是从这里下载的,里面翻译一下找到自己的版本下载就行。

解压,然后放到,D 盘?E 盘?哪个盘的哪个位置都行,当一个样板或直接在上面做网站。

如果按照上面的链接,你需要复制一个 php.ini-development,粘贴在原文件夹,重命名为 php.ini

这里有个坑:千万不要下载 phpStudy 或所谓的 php 编辑器,因为那基本上要么和你想要的不是一个东西,要么要花钱。

数据库

这里我比较推荐 MySQL,因为如果从上面的链接里下载的 php 是有自带的 mysqli 的,比较方便。

但你还需要下载一个 mysqli,下载链接:https://dev.mysql.com/downloads/installer 这个的操作步骤可以问 AI 大人,Ta 是会给你解答的。

这一部分是在样板里改。

但是,你需要开启这个 mysqli,在 php.ini 中改,例如下图 898 行处,把前面的分号删了:

别的啥也不用改。

开始写网站

做一个网站最重要的就是开始写!

先复制一个样板过来,重命名任意。

接下来用管理员权限运行你的 Windows PowerShell。

用 cd 命令切到你的项目主文件夹,例如:你的项目主文件夹为 D:/www/,那你就写:

cd D:/www

然后,让 php 能够运行到网页上(此处的 80 是端口号,可以改):

php -S 127.0.0.1:80

这样你访问 http://127.0.0.1:80 就能出来你的网站了。

写网站

初始化数据库

在网站主文件夹下写一个 database.php,可以参考我的。 ::::success[database.php]{open} 这一段不用怀疑,是 AI 大人写的。

<?php

// 连接参数
$host = 'localhost';
$user = 'root';
$pass = '';  // 你的 MySQL 密码

$link = mysqli_connect($host, $user, $pass);

if ($link) {

// SQL 文件路径
$sql_file = $_SERVER['DOCUMENT_ROOT'].'cc';

if (!file_exists($sql_file)) {
    die("❌ SQL 文件不存在: $sql_file\n");
}

// 读取 SQL 文件内容
$sql_content = file_get_contents($sql_file);

// 分割 SQL 语句(处理分号,但保留存储过程中的分号)
$queries = [];
$current_query = '';
$in_string = false;
$string_char = '';

for ($i = 0; $i < strlen($sql_content); $i++) {
    $char = $sql_content[$i];

    // 处理字符串
    if ($char === "'" || $char === '"') {
        if (!$in_string) {
            $in_string = true;
            $string_char = $char;
        } elseif ($char === $string_char) {
            // 检查是否是转义
            if ($i > 0 && $sql_content[$i-1] !== '\\') {
                $in_string = false;
            }
        }
    }

    $current_query .= $char;

    // 不在字符串中且遇到分号,结束当前语句
    if (!$in_string && $char === ';') {
        $queries[] = trim($current_query);
        $current_query = '';
    }
}

// 添加最后一个查询(如果没有分号结尾)
if (!empty(trim($current_query))) {
    $queries[] = trim($current_query);
}

// 执行每个查询
$success = 0;
$failed = 0;

foreach ($queries as $index => $query) {
    if (empty(trim($query))) {
        continue;
    }

    $query_num = $index + 1;
    echo "[$query_num] 执行: " . substr($query, 0, 60) . "... ";

    if ($link->query($query)) {
        echo "✅ 成功\n";
        $success++;
    } else {
        echo "❌ 失败: " . $link->error . "\n";
        $failed++;
    }
}

echo "\n📊 执行结果:\n";
echo "✅ 成功: $success 条\n";
echo "❌ 失败: $failed 条\n";

} else {
    echo "❌ 连接失败: " . mysqli_connect_error() . "\n";

    echo "\n可能原因:\n";
    echo "1. MySQL 服务未运行 - 运行: net start MySQL80\n";
    echo "2. 密码错误 - 你设置的密码是: '$pass'\n";
    echo "3. 端口被占用 - 检查端口 3306\n";
}
?>

::::

但是先别运行。

接下来再新建一个 /db/app_db233.sql,里面先写:

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+08:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `app_db233` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;

USE `app_db233`;

接下来就是新建各种数据表,比如。

/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `user_id` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `username` varchar(20) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `user_id`
--

LOCK TABLES `user_id` WRITE;
/*!40000 ALTER TABLE `user_id` DISABLE KEYS */;
/*!40000 ALTER TABLE `user_id` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `user_info`
--

/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `user_info` (
    `username` varchar(20) NOT NULL,
    `password` char(32) NOT NULL,
    `encrypt` int(11) NOT NULL DEFAULT '0',
    `decrypt` int(11) NOT NULL DEFAULT '0',
    `color` varchar(32) NOT NULL DEFAULT 'cyan',
    `manage` char(1) DEFAULT NULL,
    `banned` char(1) DEFAULT NULL,
    `muted` char(1) DEFAULT NULL,
    PRIMARY KEY (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `user_info`
--

LOCK TABLES `user_info` WRITE;
/*!40000 ALTER TABLE `user_info` DISABLE KEYS */;
/*!40000 ALTER TABLE `user_info` ENABLE KEYS */;
UNLOCK TABLES;

最后可以运行 /database.php 了。

运行方法:浏览器访问 http://127.0.0.1:80/database.php 进行初始化数据库。

网站主体可以用 Route 实现

我建议写一个 Route,可以直接套用我这个,在主文件夹下建很多文件可以参考下图:

主文件夹
      |- models
      |       |- Route.php
      |- index.php
      |- route.php

注:不包括已有文件。

这里的所有文件都可以抄我的:

::::success[/models/Route.php]{open} 这个显然不是 AI 大人写的。

<?php

class Route {
    protected static $routes = array();
    protected static $patterns = array();
    protected static $groupStack = array(array());

    public static function match($methods, $uri, $action) {
        return self::addRoute(array_map('strtoupper', (array)$methods), $uri, $action);
    }
    public static function any($uri, $action) {
        return self::addRoute(array('GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'), $uri, $action);
    }
    public static function get($uri, $action) {
        return self::addRoute(['GET', 'HEAD'], $uri, $action);
    }
    public static function post($uri, $action) {
        return self::addRoute('POST', $uri, $action);
    }
    public static function put($uri, $action) {
        return self::addRoute('PUT', $uri, $action);
    }
    public static function patch($uri, $action) {
        return self::addRoute('PATCH', $uri, $action);
    }
    public static function delete($uri, $action) {
        return self::addRoute('DELETE', $uri, $action);
    }

    public static function group(array $attributes, Closure $callback) {
        self::$groupStack[] = array_merge(self::getGroup(), $attributes);
        call_user_func($callback);
        array_pop(self::$groupStack);
    }
    public static function getGroup() {
        return self::$groupStack[count(self::$groupStack) - 1];
    }

    public static function pattern($name, $pat) {
        self::$patterns[$name] = $pat;
    }

    public static function dispatch() {
        foreach (self::$routes as $route) {
            if (self::checkRoute($route)) {
                include $_SERVER['DOCUMENT_ROOT'].$route['action'];
                return $route;
            }
        }
        // 你个人的 404 页面
    }

    protected static function addRoute($methods, $uri, $action) {
        if (is_string($methods)) {
            $methods = [$methods];
        }

        $cur = array();
        $cur['methods'] = $methods;
        $cur['uri'] = rtrim($uri, '/');
        $cur['action'] = $action;
        $cur = array_merge(self::getGroup(), $cur);
        self::$routes[] = $cur;

        return $cur;
    }
    public static function httpHost() {
        if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
            return $_SERVER['HTTP_X_FORWARDED_HOST'];
        } elseif (isset($_SERVER['HTTP_HOST'])) {
            return $_SERVER['HTTP_HOST'];
        } else {
            return $_SERVER['SERVER_NAME'].($_SERVER['SERVER_PORT'] == '80' ? '' : ':'.$_SERVER['SERVER_PORT']);
        }
    }
    public static function requestPath() {
        $uri = $_SERVER['REQUEST_URI'];
        $p = strpos($uri, '?');
        if ($p === false) {
            return $uri;
        } else {
            return substr($uri, 0, $p);
        }
    }
    protected static function checkRoute($route) {
        if (!in_array($_SERVER['REQUEST_METHOD'], $route['methods'])) {
            return false;
        }

        $rep_arr = array();
        foreach (self::$patterns as $name => $pat) {
            $rep_arr['{'.$name.'}'] = "(?P<$name>$pat)";
        }
        $rep_arr['/'] = '\/';
        $rep_arr['.'] = '\.';

        $matches = array();
        if (isset($route['domain'])) {
            $domain_pat = strtr($route['domain'], $rep_arr);
            if (!preg_match('/^'.$domain_pat.'$/', self::httpHost(), $domain_matches)) {
                return false;
            }
            $matches = array_merge($matches, $domain_matches);
        }

        $uri_pat = strtr($route['uri'], $rep_arr);
        if (!preg_match('/^'.$uri_pat.'$/', rtrim(self::requestPath(), '/'), $uri_matches)) {
            return false;
        }
        $matches = array_merge($matches, $uri_matches);

        foreach ($matches as $key => $val) {
            if (!is_numeric($key)) {
                $_GET[$key] = $val;
            }
        }

        return true;
    }
}

注意要改一下第 50 行。 :::: ::::success[route.php]{open}

<?php

Route::pattern('username', '[a-zA-Z0-9_]{1,20}');
Route::pattern('id', '[1-9][0-9]{0,9}');
// 如果还有还可以加,左边是字符串名,右边是格式

Route::group([
        'domain' => ''// 网站域名,不加 'http://'
    ], function() {
        Route::any('/', '/app/index.php');
        Route::any('/user/{username}', '/app/user_info.php');
        // 还可以加,左边是网站链接,右边是本地具体文件
    }
);

:::: ::::success[index.php]{open}

<?php
require $_SERVER['DOCUMENT_ROOT'].'/models/Route.php';
require $_SERVER['DOCUMENT_ROOT'].'/route.php';
Route::dispatch();
?>

::::

其他文件都要按照网站主体来走

建议大家按照我的这个来,后面的文件/文件夹:

主文件夹
      |- app
      |    |- index.php // 真正的主页
      |    |- // 其他文件,但都要是 route.php 里出现的
      |- views
      |      |- header.php
      |      |- footer.php
      |      |- // 其他特殊的,比如:main.php
      |- libs
      |     |- html-lib.php
      |     |- query-lib.php
      |     |- rand-lib.php
      |     |- security-lib.php
      |     |- validate-lib.php

下面是分别解释

/app/ 的文件是网站最终显示的页面

基本是这个结构

<!DOCTYPE html>
<html>
<?php
$host = 'localhost';
$user = 'root';
$pass = '';// 密码
$DB = mysqli_connect($host, $user, $pass);
$DB->query('USE `app_db233`;');
include $_SERVER['DOCUMENT_ROOT'].'/libs/rand-lib.php';
include $_SERVER['DOCUMENT_ROOT'].'/libs/validate-lib.php';
include $_SERVER['DOCUMENT_ROOT'].'/libs/query-lib.php';
include $_SERVER['DOCUMENT_ROOT'].'/libs/html-lib.php';
include $_SERVER['DOCUMENT_ROOT'].'/libs/security-lib.php';
?>
<head>
    <meta charset="utf-8" />
    <title>标题</title>
    <?php include $_SERVER['DOCUMENT_ROOT'].'/views/header.php'; ?>
</head>

<body>
    正文
    <?php include $_SERVER['DOCUMENT_ROOT'].'/views/footer.php'; ?>
</body>

</html>

有了上面的代码结构写 /views/ 的代码就不难了

基本大家都知道要写什么了。

/libs/ 基本要写的东西不多

</html>'; die(); } function become404Page() { becomeMsgPage('<div class="text-center"><div style="font-size:233px">404</div><p>唔……未找到该页面……你是从哪里点进来的……>_<……</p></div>', '404'); } function become403Page() { becomeMsgPage('<div class="text-center"><div style="font-size:233px">403</div><p>禁止入内! T_T</p></div>', '403'); } function getClickZanBlock(type, id, cnt, user, val = null) { if (val == null) {

} if ($user == user()) return '<div class="uoj-click-zan-block" data-id="'.$id.'" data-type="'.$type.'" data-val="'.$val.'" data-cnt="'.$cnt.'"></div>'; return '<div class="uoj-click-zan-block" data-id="'.$id.'" data-type="'.$type.'" data-val="'.$val.'" data-cnt="'.$cnt.'" data-user="'.$user.'"></div>'; } function getUserLink($username) { $host = 'localhost'; $user = 'root'; $pass = ''; $DB = mysqli_connect($host, $user, $pass); $DB->query('USE `app_zyxot233`;'); $text = $username; if (strlen($username) && $username[0] == '@') $username = substr($username, 1); if ($username == '加密系统官方') return '<span class="uoj-username" data-color="red">'.$text.'</span>'; if (!$DB->query("select `password` from `user_info` where `username` = '$username'")->num_rows) return '/'; $color = ' data-color="'.$DB->query("select `color` from `user_info` where `username` = '$username'")->fetch_assoc()['color'].'"'; $banned = ''; if ($DB->query("select `banned` from `user_info` where `username` = '$username'")->fetch_assoc()['banned'] == '1') $banned = ' data-banned="1"'; return '<span class="uoj-username"'.$color.$banned.'>'.$text.'</span>'; } function getUserSpan($username) { $host = 'localhost'; $user = 'root'; $pass = ''; $DB = mysqli_connect($host, $user, $pass); $DB->query('USE `app_zyxot233`;'); $text = $username; if (strlen($username) && $username[0] == '@') $username = substr($username, 1); if ($username == '加密系统官方') return '<span class="uoj-username" data-color="red" data-link="0">'.$text.'</span>'; if (!$DB->query("select `password` from `user_info` where `username` = '$username'")->num_rows) return '/'; $color = ' data-color="'.$DB->query("select `color` from `user_info` where `username` = '$username'")->fetch_assoc()['color'].'"'; $banned = ''; if ($DB->query("select `banned` from `user_info` where `username` = '$username'")->fetch_assoc()['banned'] == '1') $banned = ' data-banned="1"'; return '<span class="uoj-username"'.$color.$banned.' data-link="0">'.$text.'</span>'; } ?> ``` :::: - `/libs/query-lib.php` 一般写请求一个键值在 DB 中的所有信息。 ::::info[示例代码,我的网站的]{open} ```php line-numbers <?php function queryUser($username) { $host = 'localhost'; $user = 'root'; $pass = ''; $DB = mysqli_connect($host, $user, $pass); $DB->query('USE `app_zyxot233`;'); if (!validateUsername($username)) { return null; } return $DB->query("select * from user_info where username='" . $DB->real_escape_string($username) . "'")->fetch_assoc(); } function queryZanVal($id, $type, $username) { $host = 'localhost'; $user = 'root'; $pass = ''; $DB = mysqli_connect($host, $user, $pass); $DB->query('USE `app_zyxot233`;'); if (!$username) { return 0; } $esc_type = $DB->real_escape_string($type); $row = $DB->query("select val from click_zans where username='".$username."' and type='$esc_type' and target_id='$id'"); if (!$row->num_rows) { return 0; } return $row->fetch_array(MYSQLI_ASSOC)['val']; } ``` :::: - `/libs/rand-lib.php` 一般写在用于登录、注册时的随机数。 ::::info[示例代码,我的网站的]{open} ```php line-numbers <?php function uojRand($l, $r) { return mt_rand($l, $r); } function uojRandString($len, $charset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') { $n_chars = strlen($charset); $str = ''; for ($i = 0; $i < $len; $i++) { $str .= $charset[uojRand(0, $n_chars - 1)]; } return $str; } ``` :::: - `/libs/security-lib.php` 一般写有关安全的问题,比如用户的密码…… ::::info[示例代码,我的网站的]{open} ```php line-numbers <?php function getPasswordToStore($password, $username) { return md5($username . $password); } function checkPassword($username, $password) { $host = 'localhost'; $user = 'root'; $pass = ''; $DB = mysqli_connect($host, $user, $pass); $DB->query('USE `app_zyxot233`;'); return $DB->query("select `password` from `user_info` where `username` = '$username'")->fetch_assoc()['password'] == md5($username . $password); } function getPasswordClientSalt() { return "salt0"; } function crsf_token() { if (!isset($_SESSION['_token'])) { $_SESSION['_token'] = uojRandString(60); } return $_SESSION['_token']; } function crsf_check() { if (isset($_POST['_token'])) { $_token = $_POST['_token']; } elseif (isset($_GET['_token'])) { $_token = $_GET['_token']; } else { return false; } return $_token === $_SESSION['_token']; } function crsf_defend() { if (!crsf_check()) { becomeMsgPage('This page has expired.'); } } ``` :::: - `/libs/validate-lib.php` 一般写有关检测格式对不对(如用户名、密码)、合不合法的内容…… ::::warning[参考代码,我的网站的]{open} 由于此文件中会有一些涉及用户密码、checksum 所以不能给大家参考所有的了。 ```php line-numbers <?php function validateUsername($username) { return is_string($username) && preg_match('/^[a-zA-Z0-9_]{1,20}$/', $username); } function validatePassword($password) { return is_string($password) && preg_match('/^[a-z0-9]{32}$/', $password); } function validateEmail($email) { return is_string($email) && strlen($email) <= 50 && preg_match('/^(.+)@(.+)$/', $email); } function validateQQ($QQ) { return is_string($QQ) && strlen($QQ) <= 15 && preg_match('/^[0-9]{5,15}$/', $QQ); } function validateMotto($motto) { return is_string($motto) && ($len = mb_strlen($motto, 'UTF-8')) !== false && $len <= 50; } function validateUInt($x) { // [0, 1000000000) if (!is_string($x)) { return false; } if ($x === '0') { return true; } return preg_match('/^[1-9][0-9]{0,8}$/', $x); } function validateInt($x) { if (!is_string($x)) { return false; } if ($x[0] == '-') { $x = substr($x, 1); } return validateUInt($x); } function validateUploadedFile($name) { return isset($_FILES[$name]) && is_uploaded_file($_FILES[$name]['tmp_name']); } function validateIP($ip) { return filter_var($ip, FILTER_VALIDATE_IP) !== false; } ``` :::: ## 内网穿透 接下来就是内网穿透了,我用的是 zeronews,我不知道这个让不让说,我就提一下我用的是什么吧,别的我就不说了,这种问题网上也有很多教程,大家可以参考。 ## 最后,网站就完成了 这种方法最后的网站是以自己电脑做服务器的,所以可能有的时候电脑不开访问不了。 但这个问题解决不了,除非不用自己电脑,去网上找一些可以托管网站的网站也行,如果不找的话问题其实也不是很大。 这样大家的网站就完成了,可以把域名分享在评论区哟~ 在这里我也分(xuan)享(chuan)一下我的网站 [https://fx6d4ukd61.fy.takin.cc](https://fx6d4ukd61.fy.takin.cc),其实是和 [zyx](https://www.luogu.com.cn/user/1934210) 一起做的,他是主页面算法设计者,我是网站设计者,因为是拿我自己电脑做服务器,所以可能会不稳定(有的时候访问不了)qwq 感谢大家的时间,下次再见(挖坑)!