Nhà soạn nhạc có yêu cầu phpoffice/phpspreadsheet không có Sudo không?

Tôi muốn viết Script bằng gói Phpspreadsheet. Tôi không có kinh nghiệm về php. Tôi đang cố gắng thêm tham chiếu vào tập lệnh của mình thông qua sudo composer require phpoffice/phpspreadsheet. Nhưng nó không hoạt động. tôi đang nhận được lỗi. Cài đặt không thành công, đang xóa trình soạn nhạc. json…. Nếu không biết tại sao nó không hoạt động. Bất kỳ ý tưởng?

lỗi chi tiết

Your requirements could not be resolved to an installable set of packages.

Problem 1
- Installation request for phpoffice/phpspreadsheet ^1.0 -> satisfiable by phpoffice/phpspreadsheet[1.0.0].
- phpoffice/phpspreadsheet 1.0.0 requires ext-dom * -> the requested PHP extension dom is missing from your system.

To enable extensions, verify that they are enabled in those .ini files:
- /etc/php/7.0/cli/php.ini
- /etc/php/7.0/cli/conf.d/10-mysqlnd.ini
- /etc/php/7.0/cli/conf.d/10-opcache.ini
- /etc/php/7.0/cli/conf.d/10-pdo.ini
- /etc/php/7.0/cli/conf.d/20-calendar.ini
- /etc/php/7.0/cli/conf.d/20-ctype.ini
- /etc/php/7.0/cli/conf.d/20-exif.ini
- /etc/php/7.0/cli/conf.d/20-fileinfo.ini
- /etc/php/7.0/cli/conf.d/20-ftp.ini
- /etc/php/7.0/cli/conf.d/20-gettext.ini
- /etc/php/7.0/cli/conf.d/20-iconv.ini
- /etc/php/7.0/cli/conf.d/20-json.ini
- /etc/php/7.0/cli/conf.d/20-mcrypt.ini
- /etc/php/7.0/cli/conf.d/20-mysqli.ini
- /etc/php/7.0/cli/conf.d/20-pdo_mysql.ini
- /etc/php/7.0/cli/conf.d/20-phar.ini
- /etc/php/7.0/cli/conf.d/20-posix.ini
- /etc/php/7.0/cli/conf.d/20-readline.ini
- /etc/php/7.0/cli/conf.d/20-shmop.ini
- /etc/php/7.0/cli/conf.d/20-sockets.ini
- /etc/php/7.0/cli/conf.d/20-sysvmsg.ini
- /etc/php/7.0/cli/conf.d/20-sysvsem.ini
- /etc/php/7.0/cli/conf.d/20-sysvshm.ini
- /etc/php/7.0/cli/conf.d/20-tokenizer.ini
You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.

Installation failed, reverting ./composer.json to its original content.

Giải pháp tốt nhất

Vì vậy, tôi nên cài đặt

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
9

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
0

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
1

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
2

Giải pháp liên quan

Php – PHP ‘foreach’ thực sự hoạt động như thế nào

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 hỗ trợ lặp qua ba loại giá trị khác nhau

  • Mảng
  • đối tượng bình thường
  • // Using by-ref iteration here to make sure that it's really
    // the same array in both loops and not a copy
    foreach ($arr as &$v1) {
        foreach ($arr as &$v) {
            // ...
        }
    }
    
    4 đối tượng

Sau đây, tôi sẽ cố gắng giải thích chính xác cách thức hoạt động của phép lặp trong các trường hợp khác nhau. Cho đến nay, trường hợp đơn giản nhất là các đối tượng

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
4, đối với các đối tượng
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 này về cơ bản chỉ là đường cú pháp cho mã dọc theo các dòng này

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}

Đối với các lớp nội bộ, tránh các cuộc gọi phương thức thực tế bằng cách sử dụng API nội bộ về cơ bản chỉ phản ánh giao diện

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
7 ở cấp độ C

Việc lặp lại các mảng và các đối tượng đơn giản phức tạp hơn nhiều. Trước hết, cần lưu ý rằng trong "mảng" PHP thực sự là các từ điển có thứ tự và chúng sẽ được duyệt theo thứ tự này (khớp với thứ tự chèn miễn là bạn không sử dụng thứ gì đó như

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
8). Điều này trái ngược với việc lặp lại theo thứ tự tự nhiên của các khóa (cách hoạt động của danh sách trong các ngôn ngữ khác) hoặc không có thứ tự xác định nào (cách hoạt động của từ điển trong các ngôn ngữ khác)

Điều tương tự cũng áp dụng cho các đối tượng, vì các thuộc tính đối tượng có thể được xem như một tên thuộc tính ánh xạ từ điển (được sắp xếp) khác với các giá trị của chúng, cộng với một số xử lý khả năng hiển thị. Trong phần lớn các trường hợp, các thuộc tính đối tượng không thực sự được lưu trữ theo cách không hiệu quả này. Tuy nhiên, nếu bạn bắt đầu lặp lại một đối tượng, biểu diễn đóng gói thường được sử dụng sẽ được chuyển đổi thành một từ điển thực. Tại thời điểm đó, phép lặp của các đối tượng đơn giản trở nên rất giống với phép lặp của mảng (đó là lý do tại sao tôi không thảo luận nhiều về phép lặp đối tượng đơn giản ở đây)

Càng xa càng tốt. Lặp đi lặp lại một từ điển không thể quá khó, phải không? . Có nhiều cách điều này có thể xảy ra

  • Nếu bạn lặp theo tham chiếu bằng cách sử dụng
    // Using by-ref iteration here to make sure that it's really
    // the same array in both loops and not a copy
    foreach ($arr as &$v1) {
        foreach ($arr as &$v) {
            // ...
        }
    }
    
    9 thì
    function iterate($arr) {
        foreach ($arr as $v) {}
    }
    
    $outerArr = [0, 1, 2, 3, 4];
    iterate($outerArr);
    
    0 sẽ được chuyển thành tham chiếu và bạn có thể thay đổi nó trong quá trình lặp
  • Trong PHP 5, điều tương tự cũng áp dụng ngay cả khi bạn lặp lại theo giá trị, nhưng trước đó mảng là một tham chiếu.
    function iterate($arr) {
        foreach ($arr as $v) {}
    }
    
    $outerArr = [0, 1, 2, 3, 4];
    iterate($outerArr);
    
    1
  • Các đối tượng có ngữ nghĩa đi qua xử lý, đối với hầu hết các mục đích thực tế có nghĩa là chúng hoạt động giống như các tham chiếu. Vì vậy, các đối tượng luôn có thể được thay đổi trong quá trình lặp lại

Vấn đề với việc cho phép sửa đổi trong quá trình lặp lại là trường hợp phần tử bạn đang bật bị xóa. Giả sử bạn sử dụng một con trỏ để theo dõi xem bạn đang ở phần tử mảng nào. Nếu phần tử này hiện được giải phóng, bạn chỉ còn lại một con trỏ lơ lửng (thường dẫn đến lỗi phân tách)

Có nhiều cách khác nhau để giải quyết vấn đề này. PHP 5 và PHP 7 khác nhau đáng kể về vấn đề này và tôi sẽ mô tả cả hai hành vi sau đây. Tóm tắt là cách tiếp cận của PHP 5 khá ngu ngốc và dẫn đến tất cả các loại vấn đề về trường hợp cạnh kỳ lạ, trong khi cách tiếp cận liên quan nhiều hơn của PHP 7 dẫn đến hành vi nhất quán và dễ đoán hơn

Như một sơ bộ cuối cùng, cần lưu ý rằng PHP sử dụng đếm tham chiếu và sao chép khi ghi để quản lý bộ nhớ. Điều này có nghĩa là nếu bạn "sao chép" một giá trị, bạn thực sự chỉ sử dụng lại giá trị cũ và tăng số lượng tham chiếu (refcount) của nó. Chỉ khi bạn thực hiện một số loại sửa đổi, một bản sao thực sự (được gọi là "sao chép") sẽ được thực hiện. Xem Bạn đang bị lừa dối để giới thiệu rộng rãi hơn về chủ đề này

PHP5

Con trỏ mảng bên trong và HashPulum

Mảng trong PHP 5 có một "con trỏ mảng bên trong" (IAP) chuyên dụng, hỗ trợ đúng các sửa đổi. Bất cứ khi nào một phần tử bị xóa, sẽ có kiểm tra xem IAP có trỏ đến phần tử này không. Nếu đúng như vậy, thay vào đó, nó sẽ được chuyển sang phần tử tiếp theo

Trong khi

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 sử dụng IAP, có một vấn đề phức tạp khác. Chỉ có một IAP, nhưng một mảng có thể là một phần của nhiều vòng lặp
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}

Để hỗ trợ hai vòng lặp đồng thời chỉ với một con trỏ mảng bên trong,

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 thực hiện các trò tai quái sau. Trước khi phần thân vòng lặp được thực thi,
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 sẽ sao lưu một con trỏ tới phần tử hiện tại và hàm băm của nó thành một giá trị cho mỗi lần truy cập
function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
6. Sau khi thân vòng lặp chạy, IAP sẽ được đặt trở lại thành phần này nếu nó vẫn tồn tại. Tuy nhiên, nếu phần tử đã bị xóa, chúng tôi sẽ chỉ sử dụng bất cứ nơi nào IAP hiện đang ở. Kế hoạch này chủ yếu là loại hoạt động, nhưng có rất nhiều hành vi kỳ lạ mà bạn có thể thoát khỏi nó, một số trong số đó tôi sẽ chứng minh bên dưới

sao chép mảng

IAP là một tính năng hiển thị của một mảng (được hiển thị thông qua họ hàm

function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
7), vì những thay đổi như vậy đối với IAP được coi là sửa đổi theo ngữ nghĩa sao chép khi ghi. Thật không may, điều này có nghĩa là trong nhiều trường hợp,
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 buộc phải sao chép mảng mà nó đang lặp lại. Các điều kiện chính xác là

  1. Mảng không phải là tham chiếu (is_ref=0). Nếu đó là một tài liệu tham khảo, thì những thay đổi đối với nó được cho là sẽ lan truyền, vì vậy nó không được sao chép
  2. Mảng có refcount>1. Nếu
    function iterate($arr) {
        foreach ($arr as $v) {}
    }
    
    $outerArr = [0, 1, 2, 3, 4];
    iterate($outerArr);
    
    9 là 1, thì mảng không được chia sẻ và chúng tôi có thể tự do sửa đổi trực tiếp

Nếu mảng không trùng lặp (is_ref=0, refcount=1) thì chỉ có

function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
9 của nó được tăng lên (*). Ngoài ra, nếu sử dụng
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 theo tham chiếu, thì mảng (có khả năng trùng lặp) sẽ được chuyển thành tham chiếu

Hãy coi mã này là một ví dụ nơi xảy ra sự trùng lặp

function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);

Tại đây,

function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
0 sẽ được sao chép để ngăn các thay đổi IAP trên
function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
0 rò rỉ sang
reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
    code();
    move_forward(arr);
}
4. Về các điều kiện trên, mảng không phải là tham chiếu (is_ref=0) và được sử dụng ở hai vị trí (refcount=2). Yêu cầu này là không may và là một yếu tố tạo tác của việc triển khai dưới mức tối ưu (không có lo ngại về sửa đổi trong quá trình lặp lại ở đây, vì vậy chúng tôi không thực sự cần sử dụng IAP ngay từ đầu)

(*) Tăng

function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
9 ở đây nghe có vẻ vô thưởng vô phạt, nhưng vi phạm ngữ nghĩa copy-on-write (COW). Điều này có nghĩa là chúng ta sẽ sửa đổi IAP của một mảng refcount=2, trong khi COW chỉ ra rằng các sửa đổi chỉ có thể được thực hiện trên các giá trị refcount=1. Vi phạm này dẫn đến thay đổi hành vi mà người dùng có thể nhìn thấy (trong khi COW thường trong suốt) vì thay đổi IAP trên mảng lặp sẽ có thể quan sát được -- nhưng chỉ cho đến khi sửa đổi không phải IAP đầu tiên trên mảng. Thay vào đó, ba tùy chọn "hợp lệ" sẽ là a) luôn trùng lặp, b) không tăng
function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
9 và do đó cho phép mảng lặp được sửa đổi tùy ý trong vòng lặp hoặc c) hoàn toàn không sử dụng IAP (

Thứ tự thăng tiến chức vụ

Có một chi tiết triển khai cuối cùng mà bạn phải biết để hiểu đúng các mẫu mã bên dưới. Cách lặp "bình thường" thông qua một số cấu trúc dữ liệu sẽ trông giống như thế này trong mã giả

reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
    code();
    move_forward(arr);
}

Tuy nhiên,

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3, là một bông tuyết khá đặc biệt, chọn cách làm hơi khác một chút

reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
    move_forward(arr);
    code();
}

Cụ thể, con trỏ mảng đã được di chuyển về phía trước trước khi thân vòng lặp chạy. Điều này có nghĩa là trong khi thân vòng lặp đang hoạt động trên phần tử

reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
    code();
    move_forward(arr);
}
8, thì IAP đã ở phần tử
reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
    code();
    move_forward(arr);
}
9. Đây là lý do tại sao các mẫu mã hiển thị sửa đổi trong quá trình lặp lại sẽ luôn
reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
    move_forward(arr);
    code();
}
0 phần tử tiếp theo, thay vì phần tử hiện tại

ví dụ. Các trường hợp thử nghiệm của bạn

Ba khía cạnh được mô tả ở trên sẽ cung cấp cho bạn ấn tượng gần như đầy đủ về các đặc điểm riêng của việc triển khai

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 và chúng ta có thể chuyển sang thảo luận về một số ví dụ

Hành vi của các trường hợp thử nghiệm của bạn rất đơn giản để giải thích tại thời điểm này

  • Trong trường hợp thử nghiệm 1 và 2,

    reset(arr);
    while (get_current_data(arr, &data) == SUCCESS) {
        move_forward(arr);
        code();
    }
    
    2 bắt đầu với refcount=1, vì vậy nó sẽ không bị trùng lặp bởi
    // Using by-ref iteration here to make sure that it's really
    // the same array in both loops and not a copy
    foreach ($arr as &$v1) {
        foreach ($arr as &$v) {
            // ...
        }
    }
    
    3. Chỉ có
    function iterate($arr) {
        foreach ($arr as $v) {}
    }
    
    $outerArr = [0, 1, 2, 3, 4];
    iterate($outerArr);
    
    9 được tăng lên. Khi thân vòng lặp sau đó sửa đổi mảng (có refcount=2 tại thời điểm đó), sự trùng lặp sẽ xảy ra tại thời điểm đó. Foreach sẽ tiếp tục làm việc trên một bản sao chưa sửa đổi của
    reset(arr);
    while (get_current_data(arr, &data) == SUCCESS) {
        move_forward(arr);
        code();
    }
    
    2

  • Trong trường hợp thử nghiệm 3, một lần nữa mảng không bị trùng lặp, do đó,

    // Using by-ref iteration here to make sure that it's really
    // the same array in both loops and not a copy
    foreach ($arr as &$v1) {
        foreach ($arr as &$v) {
            // ...
        }
    }
    
    3 sẽ sửa đổi IAP của biến
    reset(arr);
    while (get_current_data(arr, &data) == SUCCESS) {
        move_forward(arr);
        code();
    }
    
    2. Khi kết thúc quá trình lặp lại, IAP là NULL (có nghĩa là quá trình lặp lại đã hoàn thành), mà
    reset(arr);
    while (get_current_data(arr, &data) == SUCCESS) {
        move_forward(arr);
        code();
    }
    
    8 biểu thị bằng cách trả về
    reset(arr);
    while (get_current_data(arr, &data) == SUCCESS) {
        move_forward(arr);
        code();
    }
    
    9

  • Trong trường hợp thử nghiệm 4 và 5, cả

    reset(arr);
    while (get_current_data(arr, &data) == SUCCESS) {
        move_forward(arr);
        code();
    }
    
    8 và
    foreach ($array as $val) {
        var_dump(current($array));
    }
    /* Output: 2 2 2 2 2 */
    
    1 đều là các hàm tham chiếu phụ.
    reset(arr);
    while (get_current_data(arr, &data) == SUCCESS) {
        move_forward(arr);
        code();
    }
    
    2 có một
    foreach ($array as $val) {
        var_dump(current($array));
    }
    /* Output: 2 2 2 2 2 */
    
    3 khi nó được chuyển cho họ, vì vậy nó phải được sao chép. Vì vậy,
    // Using by-ref iteration here to make sure that it's really
    // the same array in both loops and not a copy
    foreach ($arr as &$v1) {
        foreach ($arr as &$v) {
            // ...
        }
    }
    
    3 sẽ lại hoạt động trên một mảng riêng biệt

ví dụ. Ảnh hưởng của function iterate($arr) { foreach ($arr as $v) {} } $outerArr = [0, 1, 2, 3, 4]; iterate($outerArr); 7 trong foreach

Một cách hay để chỉ ra các hành vi trùng lặp khác nhau là quan sát hành vi của hàm

foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 2 2 2 2 */
6 bên trong vòng lặp
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3. Hãy xem xét ví dụ này

foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 2 2 2 2 */

Ở đây bạn nên biết rằng

foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 2 2 2 2 */
6 là một hàm by-ref (thực ra. prefer-ref), mặc dù nó không sửa đổi mảng. Nó phải hoạt động tốt với tất cả các chức năng khác như
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 2 2 2 2 */
9, tất cả đều là phụ đề. Truyền tham chiếu phụ ngụ ý rằng mảng phải được tách biệt và do đó,
reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
    move_forward(arr);
    code();
}
2 và
$ref = &$array;
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */
1 sẽ khác nhau. Lý do bạn nhận được
$ref = &$array;
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */
2 thay vì
$ref = &$array;
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */
3 cũng đã được đề cập ở trên.
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 tiến con trỏ mảng trước khi chạy mã người dùng, không phải sau. Vì vậy, mặc dù mã nằm ở phần tử đầu tiên, nhưng
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 đã nâng cao con trỏ đến phần tử thứ hai

Bây giờ hãy thử một sửa đổi nhỏ

$ref = &$array;
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */

Ở đây chúng ta có trường hợp is_ref=1 nên mảng không được sao chép (giống như trên). Nhưng bây giờ nó là một tham chiếu, mảng không còn phải được nhân đôi khi chuyển đến hàm by-ref

foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 2 2 2 2 */
6. Vì vậy,
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 2 2 2 2 */
6 và
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 hoạt động trên cùng một mảng. Tuy nhiên, bạn vẫn thấy hành vi không giống nhau, do cách
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 tiến con trỏ

Bạn có hành vi tương tự khi thực hiện lặp lại giới thiệu

foreach ($array as &$val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */

Ở đây, phần quan trọng là foreach sẽ biến

reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
    move_forward(arr);
    code();
}
2 thành is_ref=1 khi nó được lặp lại theo tham chiếu, vì vậy về cơ bản, bạn có tình huống tương tự như trên

Một biến thể nhỏ khác, lần này chúng ta sẽ gán mảng cho một biến khác

$foo = $array;
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 1 1 1 1 1 */

Ở đây, số lần đếm lại của

reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
    move_forward(arr);
    code();
}
2 là 2 khi vòng lặp bắt đầu, vì vậy lần đầu tiên chúng ta thực sự phải thực hiện sao chép trước. Do đó,
reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
    move_forward(arr);
    code();
}
2 và mảng được sử dụng bởi foreach sẽ hoàn toàn tách biệt ngay từ đầu. Đó là lý do tại sao bạn có được vị trí của IAP ở bất kỳ vị trí nào trước vòng lặp (trong trường hợp này là vị trí đầu tiên)

ví dụ. Sửa đổi trong quá trình lặp

Cố gắng giải thích cho các sửa đổi trong quá trình lặp lại là nơi bắt nguồn tất cả các rắc rối foreach của chúng tôi, vì vậy, nó phục vụ để xem xét một số ví dụ cho trường hợp này

Hãy xem xét các vòng lặp lồng nhau này trên cùng một mảng (trong đó phép lặp by-ref được sử dụng để đảm bảo rằng nó thực sự giống nhau)

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
0

Phần được mong đợi ở đây là ______69_______3 bị thiếu trong đầu ra vì phần tử ______59_______3 đã bị xóa. Điều có thể bất ngờ là vòng lặp bên ngoài dừng sau phần tử đầu tiên. Tại sao vậy?

Lý do đằng sau điều này là hack vòng lặp lồng nhau được mô tả ở trên. Trước khi thân vòng lặp chạy, vị trí IAP hiện tại và hàm băm được sao lưu vào một

function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
6. Sau thân vòng lặp, nó sẽ được khôi phục, nhưng chỉ khi phần tử vẫn tồn tại, nếu không, vị trí IAP hiện tại (bất kể nó có thể là gì) được sử dụng thay thế. Trong ví dụ trên, đây chính xác là trường hợp. Phần tử hiện tại của vòng lặp bên ngoài đã bị xóa, vì vậy nó sẽ sử dụng IAP, đã được đánh dấu là kết thúc bởi vòng lặp bên trong

Một hệ quả khác của cơ chế sao lưu + khôi phục

function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
6 là các thay đổi đối với IAP thông qua
foreach ($array as &$val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */
7, v.v. thường không ảnh hưởng đến
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3. Ví dụ: đoạn mã sau thực thi như thể
foreach ($array as &$val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */
7 hoàn toàn không có mặt

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
1

Lý do là, trong khi

foreach ($array as &$val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */
7 tạm thời sửa đổi IAP, nó sẽ được khôi phục về phần tử foreach hiện tại sau thân vòng lặp. Để buộc
foreach ($array as &$val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */
7 tạo hiệu ứng trên vòng lặp, bạn phải xóa thêm phần tử hiện tại để cơ chế sao lưu/khôi phục không thành công

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
2

Nhưng, những ví dụ đó vẫn lành mạnh. Điều thú vị thực sự bắt đầu nếu bạn nhớ rằng khôi phục

function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
6 sử dụng một con trỏ tới phần tử và hàm băm của nó để xác định xem nó có còn tồn tại hay không. Nhưng. Băm có xung đột và con trỏ có thể được sử dụng lại. Điều này có nghĩa là, với sự lựa chọn cẩn thận các khóa mảng, chúng ta có thể khiến
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 tin rằng một phần tử đã bị xóa vẫn tồn tại, vì vậy nó sẽ chuyển trực tiếp đến phần tử đó. Một ví dụ

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
3

Ở đây chúng ta thường mong đợi đầu ra

$foo = $array;
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 1 1 1 1 1 */
4 theo các quy tắc trước đó. Làm thế nào điều xảy ra là
$foo = $array;
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 1 1 1 1 1 */
5 có cùng hàm băm như phần tử bị loại bỏ
$foo = $array;
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 1 1 1 1 1 */
6 và bộ cấp phát tình cờ sử dụng lại cùng một vị trí bộ nhớ để lưu trữ phần tử. Vì vậy, foreach kết thúc trực tiếp nhảy đến phần tử mới được chèn, do đó rút ngắn vòng lặp

Thay thế thực thể lặp trong vòng lặp

Một trường hợp kỳ lạ cuối cùng mà tôi muốn đề cập, đó là PHP cho phép bạn thay thế thực thể được lặp lại trong vòng lặp. Vì vậy, bạn có thể bắt đầu lặp lại trên một mảng và sau đó thay thế nó bằng một mảng khác giữa chừng. Hoặc bắt đầu lặp trên một mảng và sau đó thay thế nó bằng một đối tượng

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
4

Như bạn có thể thấy trong trường hợp này, PHP sẽ chỉ bắt đầu lặp lại thực thể khác ngay từ đầu khi sự thay thế đã xảy ra

PHP7

Trình vòng lặp Hashtable

Nếu bạn vẫn còn nhớ, vấn đề chính với lặp mảng là làm thế nào để xử lý việc loại bỏ các phần tử giữa các lần lặp. PHP 5 đã sử dụng một con trỏ mảng bên trong (IAP) cho mục đích này, điều này hơi không tối ưu, vì một con trỏ mảng phải được kéo dài để hỗ trợ nhiều vòng lặp foreach đồng thời và tương tác với

foreach ($array as &$val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */
7, v.v. trên đó

PHP 7 sử dụng một cách tiếp cận khác, cụ thể là nó hỗ trợ tạo một số lượng tùy ý các trình vòng lặp có thể băm an toàn bên ngoài. Các trình vòng lặp này phải được đăng ký trong mảng, từ đó chúng có cùng ngữ nghĩa với IAP. Nếu một phần tử mảng bị xóa, tất cả các trình lặp hashtable trỏ đến phần tử đó sẽ được chuyển sang phần tử tiếp theo

Điều này có nghĩa là

// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 sẽ không còn sử dụng IAP nữa. Vòng lặp
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 sẽ hoàn toàn không ảnh hưởng đến kết quả của
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 2 2 2 2 */
6, v.v. và hành vi của chính nó sẽ không bao giờ bị ảnh hưởng bởi các chức năng như
foreach ($array as &$val) {
    var_dump(current($array));
}
/* Output: 2 3 4 5 false */
7, v.v.

sao chép mảng

Một thay đổi quan trọng khác giữa PHP 5 và PHP 7 liên quan đến sao chép mảng. Bây giờ IAP không còn được sử dụng nữa, phép lặp mảng theo giá trị sẽ chỉ thực hiện gia số

function iterate($arr) {
    foreach ($arr as $v) {}
}

$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
9 (thay vì sao chép mảng) trong mọi trường hợp. Nếu mảng được sửa đổi trong vòng lặp
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3, tại thời điểm đó sẽ xảy ra sự trùng lặp (theo copy-on-write) và
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
    foreach ($arr as &$v) {
        // ...
    }
}
3 sẽ tiếp tục hoạt động trên mảng cũ

Trong hầu hết các trường hợp, thay đổi này là minh bạch và không có tác dụng nào khác ngoài hiệu suất tốt hơn. Tuy nhiên, có một trường hợp nó dẫn đến hành vi khác, cụ thể là trường hợp trước đó mảng là một tham chiếu

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
5

Việc lặp lại theo giá trị trước đây của mảng tham chiếu là trường hợp đặc biệt. Trong trường hợp này, không xảy ra trùng lặp, vì vậy tất cả các sửa đổi của mảng trong quá trình lặp sẽ được phản ánh bởi vòng lặp. Trong PHP 7, trường hợp đặc biệt này không còn nữa. Lặp lại giá trị phụ của một mảng sẽ luôn tiếp tục hoạt động trên các phần tử ban đầu, bỏ qua mọi sửa đổi trong vòng lặp

Tất nhiên, điều này không áp dụng cho phép lặp tham chiếu phụ. Nếu bạn lặp theo tham chiếu, tất cả các sửa đổi sẽ được phản ánh bởi vòng lặp. Thật thú vị, điều này cũng đúng với phép lặp giá trị phụ của các đối tượng đơn giản

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
6

Điều này phản ánh ngữ nghĩa xử lý phụ của các đối tượng (i. e. chúng hoạt động giống như tham chiếu ngay cả trong ngữ cảnh phụ giá trị)

ví dụ

Hãy xem xét một vài ví dụ, bắt đầu với các trường hợp thử nghiệm của bạn

  • Trường hợp thử nghiệm 1 và 2 giữ nguyên đầu ra. Lặp lại mảng theo giá trị luôn tiếp tục hoạt động trên các phần tử ban đầu. (Trong trường hợp này, ngay cả

    foreach ($it as $k => $v) { /* .. */ }
    
    /* translates to: */
    
    if ($it instanceof IteratorAggregate) {
        $it = $it->getIterator();
    }
    for ($it->rewind(); $it->valid(); $it->next()) {
        $v = $it->current();
        $k = $it->key();
        /* .. */
    }
    
    05 và hành vi trùng lặp hoàn toàn giống nhau giữa PHP 5 và PHP 7)

  • Trường hợp thử nghiệm 3 thay đổi.

    foreach ($it as $k => $v) { /* .. */ }
    
    /* translates to: */
    
    if ($it instanceof IteratorAggregate) {
        $it = $it->getIterator();
    }
    for ($it->rewind(); $it->valid(); $it->next()) {
        $v = $it->current();
        $k = $it->key();
        /* .. */
    }
    
    06 không còn sử dụng IAP, vì vậy
    foreach ($it as $k => $v) { /* .. */ }
    
    /* translates to: */
    
    if ($it instanceof IteratorAggregate) {
        $it = $it->getIterator();
    }
    for ($it->rewind(); $it->valid(); $it->next()) {
        $v = $it->current();
        $k = $it->key();
        /* .. */
    }
    
    07 không bị ảnh hưởng bởi vòng lặp. Nó sẽ có cùng một đầu ra trước và sau

  • Test case 4 và 5 giữ nguyên.

    foreach ($it as $k => $v) { /* .. */ }
    
    /* translates to: */
    
    if ($it instanceof IteratorAggregate) {
        $it = $it->getIterator();
    }
    for ($it->rewind(); $it->valid(); $it->next()) {
        $v = $it->current();
        $k = $it->key();
        /* .. */
    }
    
    07 và
    foreach ($array as &$val) {
        var_dump(current($array));
    }
    /* Output: 2 3 4 5 false */
    
    7 sẽ nhân bản mảng trước khi thay đổi IAP, trong khi
    // Using by-ref iteration here to make sure that it's really
    // the same array in both loops and not a copy
    foreach ($arr as &$v1) {
        foreach ($arr as &$v) {
            // ...
        }
    }
    
    3 vẫn sử dụng mảng ban đầu. (Không phải là thay đổi IAP sẽ có vấn đề, ngay cả khi mảng được chia sẻ. )

Nhóm ví dụ thứ hai liên quan đến hành vi của

foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 2 2 2 2 */
6 dưới các cấu hình
foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
12 khác nhau. Điều này không còn hợp lý nữa, vì
foreach ($array as $val) {
    var_dump(current($array));
}
/* Output: 2 2 2 2 2 */
6 hoàn toàn không bị ảnh hưởng bởi vòng lặp, vì vậy giá trị trả về của nó luôn giữ nguyên

Tuy nhiên, chúng tôi nhận được một số thay đổi thú vị khi xem xét sửa đổi trong quá trình lặp lại. Tôi hy vọng bạn sẽ tìm thấy hành vi mới lành mạnh hơn. ví dụ đầu tiên

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
7

Như bạn có thể thấy, vòng lặp bên ngoài không còn bị hủy bỏ sau lần lặp đầu tiên. Lý do là cả hai vòng lặp hiện có các trình vòng lặp có thể băm hoàn toàn riêng biệt và không còn bất kỳ sự lây nhiễm chéo nào của cả hai vòng lặp thông qua một IAP được chia sẻ

Một trường hợp cạnh kỳ lạ khác hiện đã được khắc phục, đó là hiệu ứng kỳ lạ mà bạn nhận được khi xóa và thêm các phần tử có cùng hàm băm

foreach ($it as $k => $v) { /* .. */ }

/* translates to: */

if ($it instanceof IteratorAggregate) {
    $it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
    $v = $it->current();
    $k = $it->key();
    /* .. */
}
8

Trước đây, cơ chế khôi phục HashPulum đã chuyển ngay sang phần tử mới vì nó "trông" giống như phần tử đã xóa (do hàm băm và con trỏ xung đột). Vì chúng tôi không còn dựa vào hàm băm của phần tử cho bất kỳ thứ gì nên đây không còn là vấn đề nữa

Việc sử dụng Phpspreadsheet là gì?

Giới thiệu. PHPSpreadsheet là một thư viện được viết bằng PHP giúp đọc và ghi vào các loại định dạng tệp bảng tính khác nhau với sự trợ giúp của một nhóm lớp nhất định . Các định dạng khác nhau hỗ trợ bảng tính là Excel (. xlsx), Định dạng Tài liệu Mở(. ods),Bảng tínhML(.

Làm cách nào để cài đặt PHPSpreadsheet trong CodeIgniter 3?

Cài đặt .
Tải xuống và cài đặt CodeIgniter
Sử dụng Composer để cài đặt PhpSpreadsheet vào dự án của bạn. nhà soạn nhạc yêu cầu phpoffice/phpspreadsheet
Mở ứng dụng/config/config. php và đặt đường dẫn thư mục nhà cung cấp của bạn. .
Sử dụng phpspreadsheet thư viện bên trong bộ điều khiển của bạn

PHPOffice là gì?

PHPOffice là tập hợp các thư viện PHP mã nguồn mở miễn phí để đọc và ghi tệp từ nhiều định dạng tài liệu khác nhau . Hiện tại có ba dự án liên kết với PHPOffice. PHPSpreadsheet, PHPPresentation và PHPWord.

Làm cách nào để cài đặt PhpSpreadsheet trong CentOS 7?

Cài đặt .
sudo apt-get cài đặt php7. 2-gd php7. 2-mbstring php7. 2-zip -y
sudo apt-get cài đặt php7. 4-gd php7. 4-mbstring php7. 4-zip -y
nhà soạn nhạc yêu cầu phpoffice/phpspreadsheet
$writer = new PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); . csv');