Thắc mắc về việc require file php

Giả sử mình có file index.php như sau:

$x = 1;
class index {
      public $y = 2;
      public function indexFunction() {
            global $x;
            echo $x . "<br>" . $this -> y;
  }
}

và file test.php cùng cấp với index.php:

class test {
     public function testFunction() {
          require("index.php");
          $obj = new index;
          $ac = $obj -> indexFunction();
       }
}

Bây giờ nếu mình khởi tạo đối tượng dựa vào class test trong test.php và gọi đến phương thức testFunction() thì nó không còn giá trị $x để in ra nữa ạ? Bởi mình nghĩ khi require("index.php") xong thì nó sẽ có nguyên nội dung của file index.php. Mình cảm ơn

Đọc xong không biết bạn muốn gì

ý là mình muốn hỏi là tại sao sau khi khởi tạo đối tượng từ class test và gọi đến phương thức testFunction() thì nó lại chỉ lấy được giá trị của $y trong file index.php mà không lấy được giá trị của $x.

Kiểu như: $ctrl = new test;
$action = $ctrl -> testFunction();

Theo mình nghĩ thì sau khi gọi phương thức testFunction() thì nó sẽ tiến hành require(“index.php”) -> Sẽ lấy được các giá trị có trong file index.php. Nhưng ở đây nó chỉ lấy đc $y mà không lấy được $x

  1. require, global là gì? đọc cho thật kĩ lại, chỉnh sửa code một chút thì sẽ tìm được lý do ngay

câu này mình không đưa ra câu trả lời trực tiếp, vì có trả lời thì sau này cũng sẽ có nhữg câu hỏi tương đương kiểu này mà thôi

Cú pháp không phải là tất cả, hiểu về ngôn ngữ mới có những solution hợp lý được.
Ví dụ như mục đích của require, global nó sinh ra cho việc gì? require tương đương với việc lấy hết nội dung file được require bỏ vào file chính, còn global thì là để sử dụng biến $x kia như là biến global, câu trả lời như vậy hầu như ai cũng có thể nói được.
Khi đọc qua một cú pháp gì đó, phải suy nghĩ xem người ta tạo ra cái hàm đó hay cú pháp đó nhằm mục đích gì, trường hợp thực tế nào thì cần sử dụng. những bài tập vô bổ kiểu này để mấy thầy hay mấy thằng có chút kinh nghiệm đánh đố mấy đứa con nít thôi chứ nó chả thực tế chút nào

  1. sử dụng hàm đó thì sẽ chạy ra được kết quả như thế nào?
  2. sử dụng hàm đó để làm gì?
    Lên đây thấy nhiều bài chỉ hỏi kiểu câu 1 chứ chả thấy ai hỏi câu kiểu 2 :rofl:
1 Like

Mình chắc bạn đang thắc mắc về vụ mà người ta hay gọi là dynamic import.

Mình không rành PHP, nhưng mình cũng từng là dân Compiler nên biết chút về vụ này.
Đa số việc importing thuộc dạng static import, các câu lệnh import thường để ở đầu file.

// Java
import java.util.List;
import java.util.ArrayList;

public class Foo { }
use Zend\Mvc\Controller\AbstractController;

class IndexController extends AbstractController { }

Thường khi dùng static import thì người ta chỉ import tên class, tên constant, tên biến mà không cần biết class, constant hay biến thuộc file nào. Đặc điểm chung của mấy lệnh import trong các ngôn ngữ trên (Java, PHP, …) là không có tên file.

Static import hồi cũ là sử dụng tên file, thường thấy nhất là ngôn ngữ C, C++. Nó sẽ tìm tên file rồi copy nguyên xi file gốc (stdio.h, iostream.h,…) vào luôn.

#include <stdio.h>
#include <math.h>
#include <iostream>
#include <vector>

Tuy nhiên, đối với các scripting language, điển hình là JavaScript, vẫn sử dụng các import bằng tên file, nhưng có cách để tránh việc copy-paste như C.

// CommonJS
var express = require('express');
var Todo = require('./models/Todo');

// ES6 Module
import { Component } from '@angular/core'
import { AppComponent } from './root/AppComponent'

Cách để tránh copy-paste là JavaScript chia việc thực thi thành 2 giai đoạn: compile time và runtime.

Ở giai đoạn compile time, JS đọc file từ trên xuống dưới, lọc ra các câu lệnh requireimport. Với mỗi lệnh static import, nó thực hiện đệ quy bước compile time trên file mới. Đọc import xong, nó chạy tất cả các lệnh thuộc global scope (và các lệnh thuộc local scope được gọi từ global scope). Khi chạy hết là hoàn tất bước compile time.

Trong thời gian runtime, nếu 1 file khác gọi 1 đoạn function trong file (trên) thì JS mới thực thi các câu lệnh trong local scope (bao gồm không được gọi từ global scope ở compile time).

Tóm lại, static import thì thường người ta chỉ cần import tên class, tên biến. Nếu gặp cách cũ thì có thể import tên file, nhưng cải tiến bằng cách chia thành 2 giai đoạn thực thi: compile time và runtime.


Dạng thứ hai của import là dynamic import. Đặc điểm chung của dynamic import là chỉ xảy ra ở runtime. Nếu gặp lệnh import thì mới bắt đầu import. Nếu lệnh được thực thi nhiều lần thì import bấy nhiêu lần.

Dynamic import thường gặp ở trong REPL. Nếu mở Terminal, gõ php, node, python thì chính là REPL. Khi bạn gõ mấy câu lệnh theo cú pháp static import thì thực chất nó gọi theo dynamic import luôn đó. Do đó, dạng import này thì ai cũng xài rồi nhưng lại không biết. :yum:


Một kiểu static import khác nhìn hao hao giống dynamic import là static import bị giới hạn scope, điển hình là Scala

class Foo1 {
  import Bar1

  def baz = {
    import Baz1
  }
}

class Foo2 {
  import Bar2
}

Bar1 bị giới hạn trong Foo1, Bar2 bị giới hạn trong Foo2. Foo2 không gọi được Bar1, Foo1 không gọi được Bar2. Baz1 trong method baz() chỉ gọi được trong baz() mà không gọi được trong Foo1


Mình viết tùm lum ngôn ngữ vậy thì PHP thế nào? Thú thực mình không biết cách PHP thực thi import. Mà mình cũng chắc là tài liệu về cách require, include chi tiết cũng ít ai viết. Lý do thì nó thuộc phạm trù của Compiler. Bạn muốn có câu trả lời chỉ có cách đọc PHP specification (tài liệu dành cho dân compiler phát triển PHP, không dành cho dev chỉ có nhu cầu sử dụng), hoặc có khi bạn phải tìm hiểu bộ compiler (kiến trúc, code) tạo ra PHP để trả lời câu hỏi chính xác nhất.

3 Likes

Uầy, dnh nhiều cao nhân quá :D.

M ko đọc nhiều về compiler nhưng với những gì b mô tả thì PHP sử dụng dynamic import (include,require,…), nghĩa là import “on the fly”. Việc import được thực hiện sau khi file php được compile sang opcode (sau PHP 5.3, còn trước 5.3 thì m ko rõ), opcode trong trường hợp này là: INCLUDE_OR_EVAL

Quay trở lại câu hỏi của bạn OP, m cũng đã 2-3 năm ko code PHP nên khi gặp câu hỏi khoai như vậy cũng phải đọc lại documentation từ php.net.

Chính xác là như vậy.

Cái này là do PHP có variable scope, khi bạn require('index.php') thì biến $x nằm trong scope của file index.php và khi bạn instantiate:

$obj = new index;

thì $obj nằm trong scope của file test.php vậy nên khi $obj->indexFunction() thì khi đó $x không tồn tại ở global do đó return NULL.

PS: cách sử dụng biến global hầu như còn rất ít người dùng trong codebase hiện giờ, đa phần chỉ dùng để lưu configurations và thường immutable.

3 Likes

bạn nên nói rõ hơn là global của ngữ cảnh mà code đang thực thi, nghĩa là tại thời điểm gọi biến $x global thì nó chưa có khai báo và giá trị,
chứ nói vậy thì bạn ấy cũng không rõ đâu, vì bạn ấy đang thắc mắc [vì sao thấy $x rõ ràng là global mà lúc gọi lại không có]

$x là biến global trong ngữ cảnh file index.php
khi trong hàm testFunction() require file index.php kia thì [$x lúc này trở thành biến cục bộ trong hàm đó]. gọi là có

class test {
     public function testFunction() {
          require("index.php");
          echo $x; // Là biến $x global của file index.php (nói chính xác hơn là ngữ cảnh khi chạy file index.php, còn chạy file test.php này nó chỉ đơn giản là biến cục bộ của hàm này thôi)
       }
}

Câu hỏi mở rộng: nếu require là lấy hết nội dung của file kia đổ vô chỗ require đó, thì tại sao làm bằng tay lại không chạy được :smiley: (nghĩa là copy hết đống code kia vô ngay chỗ require để trở thành 1 file đầy đủ)

3 Likes

OOP thì chả ai viết như thế cả,nên dùng use nha

1 Like
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?