Come esegui il debug degli script PHP? [chiuso]


403

Come esegui il debug degli script PHP ?

Sono a conoscenza del debug di base come l'utilizzo della segnalazione errori. Anche il debug del breakpoint in PHPEclipse è abbastanza utile.

Qual è il modo migliore (in termini di veloce e facile) per eseguire il debug in phpStorm o qualsiasi altro IDE?




40
Credo che questa sia un'ottima domanda! Quando non sai come affrontare il debug di PHP, non sai nemmeno come esprimere la tua domanda, non sai come essere più preciso di così. Quindi, potrebbe non obbedire alle regole di Stack, ma sicuramente ci aiuta molto, principianti, molto!
Mihaela,

1
da php5.4 in poi ha introdotto il nuovo debugger dell'interfaccia della riga di comando chiamato phpdbg ( phpdbg.com ). PHP5.6 verrà fornito con phpdbg predefinito.
Ganesh Patil,

1
Hai mai sentito parlare di XDebug? :)
Pratik,

Risposte:


145

Prova Eclipse PDT per configurare un ambiente Eclipse con funzionalità di debug come hai menzionato. La possibilità di entrare nel codice è un modo molto migliore per eseguire il debug rispetto al vecchio metodo di var_dump e stampare in vari punti per vedere dove il flusso va storto. Quando tutto il resto fallisce e tutto ciò che ho è SSH e vim ancora var_dump()/ die()per trovare dove il codice va a sud.


35
Dovresti usare questa funzione: kill ($ data) {die (var_dump ($ data)); } Salva digitando 10 caratteri, la migliore funzione che abbia mai scritto tbh :)
AlexMorley-Finch


2
C'è un modo per abbellire il "var_dump"?
RPDeshaies

6
@ AlexMorley-Finch Ti sollevo akill($data) { echo "<pre>"; var_dump($data); echo "</pre>"; exit; }
Francisco Presencia il

2
Il link è "recuperabile" tramite l'incredibile Web Archive , ultimo controllo al 7 maggio '15.
Gruber,


38

Questo è il mio piccolo ambiente di debug:

error_reporting(-1);
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_BAIL, 0);
assert_options(ASSERT_QUIET_EVAL, 0);
assert_options(ASSERT_CALLBACK, 'assert_callcack');
set_error_handler('error_handler');
set_exception_handler('exception_handler');
register_shutdown_function('shutdown_handler');

function assert_callcack($file, $line, $message) {
    throw new Customizable_Exception($message, null, $file, $line);
}

function error_handler($errno, $error, $file, $line, $vars) {
    if ($errno === 0 || ($errno & error_reporting()) === 0) {
        return;
    }

    throw new Customizable_Exception($error, $errno, $file, $line);
}

function exception_handler(Exception $e) {
    // Do what ever!
    echo '<pre>', print_r($e, true), '</pre>';
    exit;
}

function shutdown_handler() {
    try {
        if (null !== $error = error_get_last()) {
            throw new Customizable_Exception($error['message'], $error['type'], $error['file'], $error['line']);
        }
    } catch (Exception $e) {
        exception_handler($e);
    }
}

class Customizable_Exception extends Exception {
    public function __construct($message = null, $code = null, $file = null, $line = null) {
        if ($code === null) {
            parent::__construct($message);
        } else {
            parent::__construct($message, $code);
        }
        if ($file !== null) {
            $this->file = $file;
        }
        if ($line !== null) {
            $this->line = $line;
        }
    }
}

2
Grazie. Mi ha salvato la giornata. (Devo solo rimuovere E_STRICT)
Sec

4
assert_callcackheh
Madbreaks,

32

Xdebug e il plug-in DBGp per Notepad ++ per la caccia di bug pesanti, FirePHP per roba leggera. Veloce e sporco? Niente batte dBug .


Il plug-in DBGp non funziona con la versione corrente di notepad ++ / xdebug e non ci sono piani per risolverlo. Puoi vedere la mia discussione con il creatore collegato qui
Joe

26

XDebug è essenziale per lo sviluppo. Lo installo prima di qualsiasi altra estensione. Ti dà tracce dello stack su qualsiasi errore e puoi abilitare facilmente la profilazione.

Per una rapida occhiata all'utilizzo di una struttura di dati var_dump(). Non usarlo print_r()perché dovrai circondarlo <pre>e stampa solo una var alla volta.

<?php var_dump(__FILE__, __LINE__, $_REQUEST); ?>

Per un ambiente di debug reale il migliore che ho trovato è Komodo IDE ma costa $$.


19

PhpEd è davvero buono. È possibile entrare / uscire / uscire dalle funzioni. È possibile eseguire codice ad-hoc, ispezionare variabili, modificare variabili. È stupefacente.


4
Ho usato PhpEd e non ho parole gentili per questo rispetto a un IDE reale come NetBeans o Eclipse, né questo commento aggiunge nulla di utile alla domanda. -1
siliconrockstar,

Ho provato la maggior parte degli IDE (inclusi Zend, NetBeans ed Eclipse) prima di acquistare PhpED Professional perché era il migliore di un miglio e mezzo. Questo è accaduto qualche anno fa, quindi gli altri potrebbero essere migliorati, ma all'epoca la maggior parte di essi era dolorosamente lenta perché scritta in Java. Non capisco come qualcuno possa non avere "parole gentili per questo" quando (per me) è stato così chiaramente il migliore, la decisione è stata un gioco da ragazzi.
lm713,

17

1) Uso print_r (). In TextMate, ho uno snippet per "pre" che si espande in questo modo:

echo "<pre>";
print_r();
echo "</pre>";

2) Uso Xdebug, ma non sono riuscito a far funzionare la GUI direttamente sul mio Mac. Almeno stampa una versione leggibile della traccia dello stack.


Sono sicuro che intendi echo "</pre>"; alla fine però.
altermativ,

9
Puoi anche passare "true" nella funzione in modo che restituisca la stringa. Significa che puoi farlo:echo '<pre>', print_r($var, true), '</pre>';
DisgruntledGoat


16

In tutta onestà, una combinazione di print e print_r () per stampare le variabili. So che molti preferiscono usare altri metodi più avanzati, ma trovo che questo sia il più facile da usare.

Dirò che non l'ho apprezzato fino a quando non ho fatto un po 'di programmazione a microprocessore presso Uni e non sono stato in grado di usare nemmeno questo.


Sono contento che tu abbia menzionato print e print_r, io uso una stampa di base per vedere se il codice è stato eseguito fino a un certo punto, il che aiuta a isolare il problema.
Brad,

Uso sia print che var_dump (). Uso print per visualizzare messaggi e informazioni di debug e var_dump per indicare lo stato delle variabili mentre le cose progrediscono.
Joshua K,

14

Xdebug , di Derick Rethans, è molto bravo. L'ho usato qualche tempo fa e ho scoperto che non era così facile da installare. Una volta terminato, non capirai come sei riuscito senza di esso :-)

C'è un buon articolo su Zend Developer Zone (l'installazione su Linux non sembra più facile) e persino un plugin per Firefox , che non ho mai usato.


2
Non è solo l'installazione frustrante. Configurare Xdebug per funzionare con Eclipse può essere un incubo. Sono stato in grado di installare Xdebug su CentOS 5 ma EclipsePDT + Xdebug non vuole collaborare :(
Jahangir,


11

Uso Netbeans con XDebug e il componente aggiuntivo Easy XDebug FireFox

Il componente aggiuntivo è essenziale quando si esegue il debug di progetti MVC, poiché il modo normale in cui XDebug viene eseguito in Netbeans è registrare la sessione di dbug tramite l'URL. Con il componente aggiuntivo installato in FireFox, dovresti impostare le proprietà del tuo progetto Netbeans -> Esegui Configuratuion -> Avanzate e selezionare "Non aprire il browser Web" Ora puoi impostare i tuoi punti di interruzione e iniziare la sessione di debug con Ctrl-F5 come al solito . Apri FireFox e fai clic con il pulsante destro del mouse sull'icona Componente aggiuntivo nell'angolo in basso a destra per avviare il monitoraggio dei punti di interruzione. Quando il codice raggiunge il punto di interruzione, si arresterà e sarà possibile ispezionare gli stati delle variabili e lo stack di chiamate.



9

PhpEdit ha un debugger integrato, ma di solito finisco per usare echo (); e print_r (); alla vecchia maniera !!


8

Per i problemi davvero grintosi che richiederebbero troppo tempo per usare print_r / echo per capire utilizzo la funzione di debug del mio IDE (PhpEd). A differenza di altri IDE che ho usato, PhpEd non richiede praticamente alcuna configurazione. l'unica ragione per cui non lo uso per i problemi che incontro è che è dolorosamente lento. Non sono sicuro che la lentezza sia specifica per PhpEd o qualsiasi debugger php. PhpEd non è gratuito ma credo che utilizzi comunque uno dei debugger open-source (come XDebug precedentemente citato). Il vantaggio con PhpEd, ancora una volta, è che non richiede alcuna configurazione che in passato ho trovato davvero noiosa.


2
Il debugger PHPEd è in realtà scritto dallo stesso ragazzo che ha scritto PHPEd e sono abbastanza sicuro che non sia open source. Almeno PHPEd non viene fornito con il sorgente ma invece ha compilato .so's e .dll's.
Artem Russakovskii,

4

Il debug manuale è generalmente più veloce per me - var_dump()e debug_print_backtrace()sono tutti gli strumenti di cui hai bisogno per armare la tua logica.


3

Bene, in una certa misura dipende da dove stanno andando le cose a sud. Questa è la prima cosa che cerco di isolare, quindi userò echo / print_r () se necessario.

NB: Ragazzi sapete che è possibile passare true come secondo argomento a print_r () e restituirà l'output invece di stamparlo? Per esempio:

echo "<pre>".print_r($var, true)."</pre>";

2
Lo avvolgo in una funzione chiamata debug. Quindi posso eseguire il debug ($ var);
jdelator,

3

Uso spesso CakePHP quando Rails non è possibile. Per eseguire il debug degli errori di solito trovo il file error.lognella cartella tmp e lo coda nel terminale con il comando ...

tail -f app/tmp/logs/error.log

Ti dà il dialogo in esecuzione dalla torta di ciò che sta succedendo, il che è abbastanza utile, se vuoi produrre qualcosa nel suo codice centrale che puoi usare.

$this->log('xxxx');

Questo di solito può darti una buona idea di cosa sta succedendo / che non va.



2

Komodo IDE funziona bene con xdebug, anche per il debug remore. Richiede una quantità minima di configurazione. Tutto ciò di cui hai bisogno è una versione di php che Komodo può usare localmente per scorrere il codice su un breakpoint. Se hai lo script importato nel progetto komodo, puoi impostare i punti di interruzione con un clic del mouse proprio come lo imposteresti all'interno di Eclipse per il debug di un programma Java. Il debug remoto è ovviamente più complicato per farlo funzionare correttamente (potrebbe essere necessario mappare l'URL remoto con uno script php nell'area di lavoro) rispetto a un'impostazione di debug locale che è abbastanza facile da configurare se ci si trova su un desktop MAC o Linux .



2

Esistono molte tecniche di debug di PHP che possono farti risparmiare innumerevoli ore durante la codifica. Una tecnica di debug efficace ma di base è semplicemente attivare la segnalazione degli errori. Un'altra tecnica leggermente più avanzata prevede l'uso di istruzioni di stampa, che possono aiutare a individuare bug più inafferrabili visualizzando ciò che sta effettivamente accadendo sullo schermo. PHPeclipse è un plug-in Eclipse che può evidenziare errori di sintassi comuni e può essere utilizzato insieme a un debugger per impostare punti di interruzione.

display_errors = Off
error_reporting = E_ALL 
display_errors = On

e anche usato

error_log();
console_log();

1

In un ambiente di produzione, registro i dati rilevanti nel registro degli errori del server con error_log ().


e poi tail -f ... funziona benissimo
markus_p

1

io uso zend studio per eclipse con il debugger integrato. È ancora lento rispetto al debug con eclipse pdt con xdebug. Si spera che risolvano questi problemi, la velocità è migliorata rispetto alle ultime versioni, ma il passaggio delle cose richiede ancora 2-3 secondi. La barra degli strumenti di zend firefox semplifica davvero le cose (debug della pagina successiva, della pagina corrente, ecc.). Inoltre fornisce un profiler che comparerà il tuo codice e fornirà grafici a torta, tempi di esecuzione, ecc.


1

La maggior parte dei bug può essere trovata facilmente semplicemente var_dumpinserendo alcune delle variabili chiave, ma ovviamente dipende dal tipo di applicazione sviluppata.

Per algoritmi più complessi le funzioni step / breakpoint / watch sono molto utili (se non necessarie)


1

PHP DBG

Interactive Stepthrough PHP Debugger implementato come un modulo SAPI che può darti il ​​controllo completo sull'ambiente senza influire sulla funzionalità o sulle prestazioni del tuo codice. Mira a essere una piattaforma di debug leggera, potente e facile da usare per PHP 5.4+ ed è spedita immediatamente con PHP 5.6.

Le caratteristiche includono:

  • Debug dettagliato
  • Breakpoint flessibili (metodo di classe, funzione, file: linea, indirizzo, codice operativo)
  • Facile accesso a PHP con eval () integrato
  • Facile accesso al codice attualmente in esecuzione
  • API Userland
  • SAPI Agnostic - Facilmente integrato
  • Supporto file di configurazione PHP
  • JIT Super Globals - Crea il tuo !!
  • Supporto readline opzionale - Comodo funzionamento del terminale
  • Supporto per il debug remoto - GUI Java in bundle
  • Funzionamento facile

Vedi gli screenshot:

PHP DBG - Debug dettagliato - screenshot

PHP DBG - Debug dettagliato - screenshot

Home page: http://phpdbg.com/

Errore PHP - Migliore segnalazione degli errori per PHP

Questa è una libreria molto facile da usare (in realtà un file) per eseguire il debug degli script PHP.

L'unica cosa che devi fare è includere un file come di seguito (all'inizio sul tuo codice):

require('php_error.php');
\php_error\reportErrors();

Quindi tutti gli errori ti forniranno informazioni come backtrace, contesto di codice, argomenti di funzione, variabili del server, ecc. Ad esempio:

Errore PHP |  Migliora la segnalazione degli errori per PHP - screenshot di backtrace Errore PHP |  Migliora la segnalazione degli errori per PHP - screenshot di backtrace Errore PHP |  Migliora la segnalazione degli errori per PHP - screenshot di backtrace

Le caratteristiche includono:

  • banale da usare, è solo un file
  • errori visualizzati nel browser per richieste normali e ajaxy
  • Le richieste AJAX vengono messe in pausa, consentendoti di rieseguirle automaticamente
  • rende gli errori il più rigorosi possibile (incoraggia la qualità del codice e tende a migliorare le prestazioni)
  • frammenti di codice nell'intera traccia dello stack
  • fornisce ulteriori informazioni (come firme di funzioni complete)
  • corregge alcuni messaggi di errore che sono semplicemente sbagliati
  • evidenziazione della sintassi
  • sembra carino!
  • personalizzazione
  • accenderlo e spegnerlo manualmente
  • eseguire sezioni specifiche senza segnalazione errori
  • ignora i file che ti consentono di evitare di evidenziare il codice nella traccia dello stack
  • file dell'applicazione; questi hanno la priorità quando si verifica un errore!

Home page: http://phperror.net/

GitHub: https://github.com/JosephLenton/PHP-Error

Il mio fork (con correzioni extra): https://github.com/kenorb-contrib/PHP-Error

DTrace

Se il tuo sistema supporta la traccia dinamica DTrace (installata di default su OS X) e il tuo PHP è compilato con le sonde DTrace abilitate ( --enable-dtrace) che dovrebbero essere di default, questo comando può aiutarti a eseguire il debug dello script PHP senza tempo:

sudo dtrace -qn 'php*:::function-entry { printf("%Y: PHP function-entry:\t%s%s%s() in %s:%d\n", walltimestamp, copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2); }'

Quindi dato il seguente alias è stato aggiunto ai tuoi file rc (ad es ~/.bashrc. ~/.bash_aliases):

alias trace-php='sudo dtrace -qn "php*:::function-entry { printf(\"%Y: PHP function-entry:\t%s%s%s() in %s:%d\n\", walltimestamp, copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2); }"'

si può tracciare lo script con facile da ricordare alias: trace-php.

Ecco uno script dtrace più avanzato, salvalo in, rendilo dtruss-php.deseguibile ( chmod +x dtruss-php.d) ed esegui:

#!/usr/sbin/dtrace -Zs
# See: https://github.com/kenorb/dtruss-lamp/blob/master/dtruss-php.d

#pragma D option quiet

php*:::compile-file-entry
{
    printf("%Y: PHP compile-file-entry:\t%s (%s)\n", walltimestamp, basename(copyinstr(arg0)), copyinstr(arg1));
}

php*:::compile-file-return
{
    printf("%Y: PHP compile-file-return:\t%s (%s)\n", walltimestamp, basename(copyinstr(arg0)), basename(copyinstr(arg1)));
}

php*:::error
{
    printf("%Y: PHP error message:\t%s in %s:%d\n", walltimestamp, copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2);
}

php*:::exception-caught
{
    printf("%Y: PHP exception-caught:\t%s\n", walltimestamp, copyinstr(arg0));
}

php*:::exception-thrown
{
    printf("%Y: PHP exception-thrown:\t%s\n", walltimestamp, copyinstr(arg0));
}

php*:::execute-entry
{
    printf("%Y: PHP execute-entry:\t%s:%d\n", walltimestamp, basename(copyinstr(arg0)), (int)arg1);
}

php*:::execute-return
{
    printf("%Y: PHP execute-return:\t%s:%d\n", walltimestamp, basename(copyinstr(arg0)), (int)arg1);
}

php*:::function-entry
{
    printf("%Y: PHP function-entry:\t%s%s%s() in %s:%d\n", walltimestamp, copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2);
}

php*:::function-return
{
    printf("%Y: PHP function-return:\t%s%s%s() in %s:%d\n", walltimestamp, copyinstr(arg3), copyinstr(arg4), copyinstr(arg0), basename(copyinstr(arg1)), (int)arg2);
}

php*:::request-shutdown
{
    printf("%Y: PHP request-shutdown:\t%s at %s via %s\n", walltimestamp, basename(copyinstr(arg0)), copyinstr(arg1), copyinstr(arg2));
}

php*:::request-startup
{
    printf("%Y, PHP request-startup:\t%s at %s via %s\n", walltimestamp, basename(copyinstr(arg0)), copyinstr(arg1), copyinstr(arg2));
}

Home page: lampada dtruss su GitHub

Ecco un semplice utilizzo:

  1. Run: sudo dtruss-php.d.
  2. Su un altro terminale eseguire: php -r "phpinfo();".

Per verificarlo, puoi andare su qualsiasi docroot con index.phped eseguire il server integrato PHP:

php -S localhost:8080

Dopodiché puoi accedere al sito all'indirizzo http: // localhost: 8080 / (o scegliere la porta più adatta a te). Da lì accedi ad alcune pagine per vedere l'output della traccia.

Nota: Dtrace è disponibile su OS X per impostazione predefinita, su Linux probabilmente hai bisogno di dtrace4linux o cerca altre alternative .

Vedi: Utilizzo di PHP e DTrace su php.net


SystemTap

In alternativa, verificare la traccia di SystemTap installando il pacchetto di sviluppo SDT di SystemTap (ad es yum install systemtap-sdt-devel.).

Ecco un esempio di script ( all_probes.stp) per tracciare tutti i punti del probe statico PHP di base per tutta la durata di uno script PHP in esecuzione con SystemTap:

probe process("sapi/cli/php").provider("php").mark("compile__file__entry") {
    printf("Probe compile__file__entry\n");
    printf("  compile_file %s\n", user_string($arg1));
    printf("  compile_file_translated %s\n", user_string($arg2));
}
probe process("sapi/cli/php").provider("php").mark("compile__file__return") {
    printf("Probe compile__file__return\n");
    printf("  compile_file %s\n", user_string($arg1));
    printf("  compile_file_translated %s\n", user_string($arg2));
}
probe process("sapi/cli/php").provider("php").mark("error") {
    printf("Probe error\n");
    printf("  errormsg %s\n", user_string($arg1));
    printf("  request_file %s\n", user_string($arg2));
    printf("  lineno %d\n", $arg3);
}
probe process("sapi/cli/php").provider("php").mark("exception__caught") {
    printf("Probe exception__caught\n");
    printf("  classname %s\n", user_string($arg1));
}
probe process("sapi/cli/php").provider("php").mark("exception__thrown") {
    printf("Probe exception__thrown\n");
    printf("  classname %s\n", user_string($arg1));
}
probe process("sapi/cli/php").provider("php").mark("execute__entry") {
    printf("Probe execute__entry\n");
    printf("  request_file %s\n", user_string($arg1));
    printf("  lineno %d\n", $arg2);
}
probe process("sapi/cli/php").provider("php").mark("execute__return") {
    printf("Probe execute__return\n");
    printf("  request_file %s\n", user_string($arg1));
    printf("  lineno %d\n", $arg2);
}
probe process("sapi/cli/php").provider("php").mark("function__entry") {
    printf("Probe function__entry\n");
    printf("  function_name %s\n", user_string($arg1));
    printf("  request_file %s\n", user_string($arg2));
    printf("  lineno %d\n", $arg3);
    printf("  classname %s\n", user_string($arg4));
    printf("  scope %s\n", user_string($arg5));
}
probe process("sapi/cli/php").provider("php").mark("function__return") {
    printf("Probe function__return: %s\n", user_string($arg1));
    printf(" function_name %s\n", user_string($arg1));
    printf("  request_file %s\n", user_string($arg2));
    printf("  lineno %d\n", $arg3);
    printf("  classname %s\n", user_string($arg4));
    printf("  scope %s\n", user_string($arg5));
}
probe process("sapi/cli/php").provider("php").mark("request__shutdown") {
    printf("Probe request__shutdown\n");
    printf("  file %s\n", user_string($arg1));
    printf("  request_uri %s\n", user_string($arg2));
    printf("  request_method %s\n", user_string($arg3));
}
probe process("sapi/cli/php").provider("php").mark("request__startup") {
    printf("Probe request__startup\n");
    printf("  file %s\n", user_string($arg1));
    printf("  request_uri %s\n", user_string($arg2));
    printf("  request_method %s\n", user_string($arg3));
}

Uso:

stap -c 'sapi/cli/php test.php' all_probes.stp

Vedi: Uso di SystemTap con le sonde statiche DTrace PHP su php.net


0

+1 per print_r (). Usalo per scaricare il contenuto di un oggetto o una variabile. Per renderlo più leggibile, fallo con un pre tag in modo da non aver bisogno di visualizzare l'origine.

echo '<pre>';
print_r($arrayOrObject);

Anche var_dump ($ thing) - questo è molto utile per vedere il tipo di sottotitoli


Una versione estesa può essere trovata qui devarticles.in/php/useful-function-to-output-debug-data-in-php
Arvind K.


0

A seconda del problema, mi piace una combinazione di error_reporting (E_ALL) mescolata a test di eco (per trovare la linea / file offensiva in cui si è verificato l'errore in modo iniziale; SAPI che non è sempre la riga / file che php ti dice giusto?), IDE brace matching (per risolvere i problemi "Errore di analisi: errore di sintassi, $ end imprevisti") e print_r (); Uscita; dump (i veri programmatori visualizzano la fonte; p).

Inoltre non puoi battere phpdebug (controlla sourceforge) con "memory_get_usage ();" e "memory_get_peak_usage ();" per trovare le aree problematiche.


0

I debugger integrati in cui è possibile osservare i valori di modifica delle variabili mentre si scorre il codice sono davvero interessanti. Tuttavia, richiedono l'installazione del software sul server e una certa quantità di configurazione sul client. Entrambi richiedono una manutenzione periodica per mantenersi in buone condizioni.

Un print_r è facile da scrivere ed è garantito per funzionare in qualsiasi configurazione.


0

Di solito trovo creare una funzione di registro personalizzata in grado di salvare su file, archiviare informazioni di debug e infine ristampare su un piè di pagina comune.

È inoltre possibile sostituire la classe di eccezione comune, in modo che questo tipo di debug sia semi-automatizzato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.