Come verificare che una stringa sia un int, ma non un doppio, ecc.?


155

PHP ha una intval()funzione che converte una stringa in un numero intero. Tuttavia, voglio verificare in anticipo che la stringa sia un numero intero, in modo da poter fornire un utile messaggio di errore all'utente se è errato. PHP ha is_int(), ma questo restituisce false per stringhe come "2".

PHP ha la is_numeric()funzione, ma restituirà vero se il numero è doppio. Voglio qualcosa che tornerà falso per un doppio, ma vero per un int.

per esempio:

my_is_int("2") == TRUE
my_is_int("2.1") == FALSE

6
come dovrebbe essere trattato "2.0"?
Nickf

Risposte:


183

Che ne dici di usare ctype_digit?

Dal manuale:

<?php
$strings = array('1820.20', '10002', 'wsl!12');
foreach ($strings as $testcase) {
    if (ctype_digit($testcase)) {
        echo "The string $testcase consists of all digits.\n";
    } else {
        echo "The string $testcase does not consist of all digits.\n";
    }
}
?>

L'esempio sopra mostrerà:

La stringa 1820.20 non è composta da tutte le cifre.
La stringa 10002 è composta da tutte le cifre.
La stringa wsl! 12 non è composta da tutte le cifre.

Funzionerà solo se il tuo input è sempre una stringa:

$numeric_string = '42';
$integer        = 42;

ctype_digit($numeric_string);  // true
ctype_digit($integer);         // false

Se il tuo input potrebbe essere di tipo int, quindi combinalo ctype_digitcon is_int.

Se ti interessano i numeri negativi, dovrai controllare l'input per un precedente -e, in tal caso, chiamare ctype_digituna substrdelle stringhe di input. Qualcosa del genere lo farebbe:

function my_is_int($input) {
  if ($input[0] == '-') {
    return ctype_digit(substr($input, 1));
  }
  return ctype_digit($input);
}

7
numeri negativi?
Anurag,

1
@Anurag, modificato in (anche se se ti interessa davvero questa roba, un regex è probabilmente più semplice).
Dominic Rodger,

4
Se vuoi verificare la presenza di numeri interi negativi, aggiungi l'uso di abs () in questo modo ctype_digit(abs($str))anziché semplicemente ctype_digit.
incantare il

3
@enchance abs('definitelynotanint') === 0. inoltre, ctype_digitprende solo stringhe
Klesun l'

Questo non è corretto Un grande numero intero in PHP è un doppio!
jgmjgm,

91

filter_var dovrebbe farlo:

var_dump(filter_var('2', FILTER_VALIDATE_INT));   // 2
var_dump(filter_var('2.0', FILTER_VALIDATE_INT)); // false
var_dump(filter_var('2.1', FILTER_VALIDATE_INT)); // false

ma

var_dump(filter_var(2, FILTER_VALIDATE_INT));     // 2
var_dump(filter_var(2.0, FILTER_VALIDATE_INT));   // 2
var_dump(filter_var(2.1, FILTER_VALIDATE_INT));   // false

Se vuoi solo i booleani come valori di ritorno, avvolgilo in una funzione, ad es

function validatesAsInt($number)
{
    $number = filter_var($number, FILTER_VALIDATE_INT);
    return ($number !== FALSE);
}

2
Mi piacciono la maggior parte delle risposte, ma penso che questa sia la più elegante.
Ian Dunn,

6
@grenoult 3v4l.org/qVRRS fornisce int 0 per stringhe e numeri interi, quindi non sai perché pensi che non funzioni. Stai confrontando il risultato con ==forse?
Gordon,

1
@Gordon mio errore, in effetti stavo confrontando solo con un if 3v4l.org/IAvCF
Guillaume Renoult,

Questo farà alcune cose strane come return true per "+123". È mitigato dal fatto che è destinato a restituire il vero int, ma potrebbe non essere quello che la gente vuole, il che è un controllo int rigoroso.
jgmjgm,

@ MohammedRédaOUASSINI l'OP ha chiesto di convalidare le stringhe , non i float. Essa vi funziona se si passa a '-1.0' (una stringa) Sarà Non lavoro se si passa a -1.0 (un galleggiante), ma questo non era un requisito per iniziare. Vedi 3v4l.org/bOX6X
Gordon l'

20

+1 alla risposta di Dominic (usando ctype_digit). Un altro modo in cui potresti farlo è con la coercizione del tipo:

$inty = "2";
$inty2 = " 2";
$floaty = "2.1";
$floaty2 = "2.0";

is_int($inty + 0); // true
is_int($floaty + 0); // false
is_int($floaty2 + 0); // false

// here's difference between this and the ctype functions.
is_int($inty2 + 0);  // true
ctype_digit($inty2); // false

2
+1 anche a te (anche se mi chiedo se quel caso particolare sarebbe gestito più chiaramente tagliando la stringa di input).
Dominic Rodger,

6
Questo metodo non funziona per stringhe non numeriche: is_int ("abc" +0) è vero
Kai Pommerenke,

1
È vero, non funziona con stringhe non numeriche ... che ne dici is_int($inty + 0) && is_numeric($inty)(so che è una soluzione pigra ma funziona per la maggior parte degli intenti e degli scopi ...
Alex Mantaut,

L'uso del jugging di tipo comporterà un controllo abbastanza libero di dove gli ints possono essere rappresentati in tutti i tipi di stringhe, non è un controllo di stringhe. Inoltre non è necessario +0. Puoi semplicemente + $ var.
jgmjgm,

13

Trasmetterlo a int. se ha ancora lo stesso valore, int;

function my_is_int($var) {
  $tmp = (int) $var;
  if($tmp == $var)
       return true;
  else
       return false;
}

2
O ancora più breve:return $var == (int) $var;
Al.G.

1
@ AI.G. Cosa succede se il cast fallisce
DR.

Questo è molto vicino, dovrebbe evitare il tipo di giocoleria anche se non necessario. Funzionerà se $ var è "123xxxx".
jgmjgm,

8
/**
 * Check if a number is a counting number by checking if it
 * is an integer primitive type, or if the string represents
 * an integer as a string
 */
function is_int_val($data) {
    if (is_int($data) === true) return true;
    if (is_string($data) === true && is_numeric($data) === true) {
        return (strpos($data, '.') === false);
    }
}

Fonte .


piuttosto che diffondere il mio codice con piccole funzioni di utilità, preferirei avere qualcosa che è incorporato in PHP.
Rory,

Non mi piacciono neanche questi hack. Ma usando questo approccio o il suggerimento ctype di Dominic, incapsulerai comunque tutta l'implementazione in un metodo. Quando uso php, ho sempre una classe "Util" per affrontare questi problemi.
GmonC,

elseifpuò essere solo ifperché sei già tornato nella dichiarazione sopra di esso.
rybo111,

Inoltre, non è necessario restituire false alla fine.
rybo111,

1
Se $dataè un oggetto, questo tornerà null. Meglio avere return false;alla fine.
Theodore R. Smith

6

Aveva bisogno di un robusto di is_intrecente. Ho trovato intval () troppo imprevedibile:

intval(array('foo', 'bar')) //returns 1 ?!?
intval("2dog") //returns 2 even though the value is definitely not an integer
intval("dog2") //also returns 2


Ho trovato questo frammento nei commenti della documentazione di PHP e dopo averlo testato, copre quasi tutto ciò che gli passi:

function my_is_int($s) {
    return (is_numeric($s) ? intval($s) == $s : false);
}


my_is_int(2); //true
my_is_int("2"); //true
my_is_int(2.1); //false
my_is_int("2.1"); //false
my_is_int("dog"); //false
my_is_int("2dog"); //false
my_is_int("dog2"); //false
my_is_int(array('foo', 'bar')); //false
my_is_int(array(1)); //false


Ma attenzione:

my_is_int(2.0); //true
my_is_int("2.0"); //true

6

Un modo davvero pulito che mi piace usare è quello. Lo lanci due volte, prima dentro int, poi dentro string, poi fai un confronto rigoroso ===. Vedi l'esempio seguente:

$value === (string)(int)$value;

Ora, riguardo alla tua funzione my_is_int, puoi fare qualcosa del genere:

function my_is_int($value){ return $value === (string)(int)$value; }
my_is_int('2');  // true
my_is_int('2.1') // false

4
function my_is_int($var) {
    return preg_match('/^\d+$/', $var);
}

1
Ho usato una variante di questo, consentendo il segnale numerico:'/^[-+]?[0-9]+$/'
Denilson Sá Maia,

Questo è lo stesso di ctype_digit, consente anche una nuova riga finale.
jgmjgm,

3

Sto usando questo:

function isInt($val){

    return (filter_var($val, FILTER_VALIDATE_INT) !== false && strpos($val, '-') === false);

}

var_dump (isInt("1"));

3

Puoi semplicemente controllare un numero, se è quindi controllare che al casting venga dato un doppio o no:

((is_numeric($var) && !is_double(1*$var)));

Solo per numeri positivi:

(is_numeric($var) && !is_double(1*$var)) && ($var >= 0)

Controllandolo:

$numbersToCheck = array("a", "-1", "1", "1.0", "1.2");

foreach ($numbersToCheck as $var) {
    echo $var . " is integer? ";var_dump((is_numeric($var) && !is_double(1*$var)));

    echo $var . " is a positive integer? ";var_dump((is_numeric($var) && !is_double(1*$var)) && ($var >= 0));
}

Produzione:

a is integer? bool(false)
a is a positive integer? bool(false)
-1 is integer? bool(true)
-1 is a positive integer? bool(false)
1 is integer? bool(true)
1 is a positive integer? bool(true)
1.0 is integer? bool(false)
1.0 is a positive integer? bool(false)
1.2 is integer? bool(false)
1.2 is a positive integer? bool(false)

is_numeric () era quello che stavo cercando.
ccjjmartin,

1
Per essere sicuro che questo è quello che vuoi, dovrai leggere la fonte PHP poiché non è ben documentata. Ad esempio, is_numeric accetta i principali spazi bianchi, che non sono documentati nel manuale di PHP. Dovresti anche tenere presente che gli interi di stringa che sono troppo grandi per essere rappresentati con un int nativo vengono convertiti in float.
jgmjgm,

2

Prova questo:

$string='12abc';
if ((int)$string==$string) var_dump((int)$string); else echo 'Invalid!';
// Outputs: Invalid!

$string='789';
if ((int)$string==$string) var_dump((int)$string); else echo 'Invalid!';
// Outputs: int 789

$string='345.00';
if ((int)$string==$string) var_dump((int)$string); else echo 'Invalid!';
// Outputs: 345

$string='123.01';
if ((int)$string==$string) var_dump((int)$string); else echo 'Invalid!';
// Outputs: Invalid!

Funziona anche se la tua stringa $ ha posizioni decimali


Questo tratterà 123xxxx come una stringa int valida.
jgmjgm,

2

Questo si occuperà anche del numero negativo

function myIsInt()
{
   return (is_numeric($var) AND (is_int($var) OR ctype_digit(trim($var, '-'))))
}
//'234-' => false
//'-234' => true
//'--234' => false
//'234' => true

Non capisco perché questo restituisca falso per 234- o --234, l'hai provato? Questo tornerà vero per ----- 1234 ----
jgmjgm l'

2
function my_is_int($var){
    return is_numeric($var) && gettype($var+0)=='integer';
}

2

Ho escogitato un modo che non riuscivo a trovare da nessuna parte, quindi lo sto inserendo qui:

Senza ulteriori indugi è questo: ctype_digit((string) abs($input))

Esempio:

function means_int($input) {
    return ctype_digit((string) abs($input));
}

$list = array(
    0,
    '0',
    1,
    '1',
    1.1,
    '1.1',
    2.0,
    '2.0',  
    2.6,
    '2.6',
    -4,
    '-4',   
    -3.2,
    '-3.2',
    -30.02,
    '-30.02',   
    100.00,
    '100.00',   
);

foreach ($list as $x) {
    var_dump($x);
    var_dump(means_int($x));
    echo PHP_EOL;
}

Risultati: (sono come previsto, suppongo)

int(0)
bool(true)

string(1) "0"
bool(true)

int(1)
bool(true)

string(1) "1"
bool(true)

float(1.1)
bool(false)

string(3) "1.1"
bool(false)

float(2)
bool(true)

string(3) "2.0"
bool(true)

float(2.6)
bool(false)

string(3) "2.6"
bool(false)

int(-4)
bool(true)

string(2) "-4"
bool(true)

float(-3.2)
bool(false)

string(4) "-3.2"
bool(false)

float(-30.02)
bool(false)

string(6) "-30.02"
bool(false)

float(100)
bool(true)

string(6) "100.00"
bool(true)

abs sta lanciando l'input da stringa a numerico. È uguale a is_int (+ $ var).
jgmjgm,

2

Forse non è il modo più performante di farlo. Ma puoi scriverlo in una riga.

function my_is_int($input) {
    return intval($input).'' === $input.'';
}

Come previsto:

    my_is_int(1);     // TRUE
    my_is_int(-1);    // TRUE
    my_is_int(1.2);   // FALSE
    my_is_int("1");   // TRUE
    my_is_int("-1");  // TRUE
    my_is_int("1.2"); // FALSE
    my_is_int(0);     // TRUE
    my_is_int(null);  // FALSE

Gotcha:

    my_is_int(1.0);   // TRUE
    my_is_int("1.0"); // FALSE

Questo è corretto, purché si assicuri che $ input sia di tipo stringa.
jgmjgm,

2

Con qualche anno di ritardo, ma sulla base delle risposte fornite qui, ho trovato una soluzione leggermente più accurata (in particolare sui booleani) e più efficiente (penso) della maggior parte delle altre risposte:

function my_is_int($s) {
    return ctype_digit($s) || is_int($s);
}

Funzionando come previsto per questi:

my_is_int(2);                   // true
my_is_int("2");                 // true
my_is_int(-2);                  // true
my_is_int(2.0);                 // false
my_is_int("2.0");               // false
my_is_int(2.1);                 // false
my_is_int("2.1");               // false
my_is_int("dog");               // false
my_is_int("2dog");              // false
my_is_int("dog2");              // false
my_is_int(array('foo', 'bar')); // false
my_is_int(array(1));            // false
my_is_int(true);                // false
my_is_int(false);               // false
my_is_int("true");              // false
my_is_int("false");             // false
my_is_int("0x101010");          // false

Tranne forse per questi 2:

my_is_int(0x101010);            // true
my_is_int("-2");                // false

molto bella. se si desidera gestire la stringa numerica con segno negativo, è possibile passare a ctype_digits (preg_replace ('/ ^ - /', '', $ s)), anche se questo inizia a diventare un po 'prolisso. inoltre, 0x101010 è un int, quindi questo non è un errore, ma piuttosto "0x101010" potrebbe essere considerato errato e sono sicuro che anche le stringhe binarie che corrispondono alla notazione letterale binaria ("0b11111") fallirebbero
fbas

1

Che ne dite di:

function isIntStr($str) {
   return preg_match('/^(-?\d+)(?:\.0+)?$/', trim($str), $ms)
       && bcComp($ms[1], PHP_INT_MAX) <= 0
       && bcComp($ms[1], -PHP_INT_MAX - 1) >= 0;
}

Questa funzione dovrebbe restituire true solo per qualsiasi numero di stringa che può essere trasmesso a int con (int)ointval() senza perdere nulla di significato matematico (come non zeri dopo il punto decimale o numeri al di fuori dell'intervallo intero di PHP) pur accettando cose che non sono matematicamente significative (come spazi bianchi; zeri iniziali; o, dopo il punto decimale, zeri esclusivamente).

Restituirà falso per '10.'ma non per '10.0'. Se volessi '10.'essere vero, potresti cambiare il +dopo l' 0espressione regolare in *.


1

Qui ho usato un po 'di codice che sembra funzionare bene e non presenta alcun problema che molti altri hanno.

if (0 != strlen(str_replace(range(0, 9), '', $TestInt))) { print 'Not an integer!';}

Non controlla l'ordine ecc., Quindi non inteso per numeri interi negativi ma con qualche codice aggiuntivo che può essere fatto anche usando alcune delle altre idee dall'alto. Può anche essere adattato per funzionare con binari array('0', '1')o esadecimali, ecc.


Questo è solo un costoso ctype_digit.
jgmjgm,

1

Guarda questo. Converte $ val in numero intero e quindi verifica se $ val convertito in stringa originale è IDENTICO (===) - solo == non funzionerà come previsto - in val intero convertito in stringa.

function validInt($val, $min=null, $max=null) {
    $ival = intval($val);
    //echo "'$ival' '$val'<br>\n"; // Uncomment to see the comparisons done in below if block
    if(''.$ival !== ''.$val) {
        return false;
    }
    if($min !== null && $ival < $min)
        return false;
    if($max !== null && $ival > $max)
        return false;
    return true;
}

Se non controlli i valori di stringa, potrebbe non funzionare come previsto:

$nums = array(
    '1',
    '+1',
    '-1',
    '01',
    '1.0',
    '.0',
    '1.123',
    'a123',
    '0x101010',
    1,
    -1,
    01,
    1.0,
    .0,
    1.123,
    0x101010,
);
foreach($nums as $num) {
    if(validInt2($num))
        echo $num." - Valid integer.<br>\n";
    else
        echo $num." - Not a valid integer.<br>\n";
}

Produzione:

1 - Valid integer.
+1 - Not a valid integer.
-1 - Valid integer.
01 - Not a valid integer.
1.0 - Not a valid integer.
.0 - Not a valid integer.
1.123 - Not a valid integer.
a123 - Not a valid integer.
0x101010 - Not a valid integer.
1 - Valid integer.
-1 - Valid integer.
1 - Valid integer.
1 - Valid integer.
0 - Valid integer.
1.123 - Not a valid integer.
1052688 - Valid integer.

Il motivo è anche se si usa hex (0x101010), ottale (01) o un numero intero memorizzato come float (1.0, 0.0), internamente tutti vengono memorizzati come float. Tuttavia, se si utilizza la funzione per verificare la presenza di int memorizzati come stringa, funzionerà.


1
public static function isNumeric($value, $negativ = false) {
    return is_int($value) || is_string($value) && (
        ctype_digit($value) || (
            $negativ && $value{0} == '-' && ctype_digit(substr($value, 1))
        )
    );

    //alternativ:
    //return $value == (int) $value;
}

Manca un segno di spunta che stringa! == ''. Se metti un intero davvero lungo in questo, esso tornerà anche vero per qualcosa che PHP lancerebbe su un float!
jgmjgm,

1

È possibile utilizzare la seguente condizione. Nota che non dovresti usare! ==

$value = 12; // true
$value = '12'; // true
$value = 'abc'; // false
$value = 12.1; // false
$value = '12.1'; // false

if (!is_numeric($value) || (int) $value != (float) $value) {
    echo "false";
} else {
    echo "true";
}

1

Funziona perfettamente! Spero che ti sarà utile =)

$value = 1; // true
$value = '1'; // true
$value = '1.0'; // true
$value = ' 1'; // true
$value = '01'; // true

$value = -1; // true
$value = '-1'; // true
$value = '-1.0'; // true
$value = ' -1'; // true
$value = '-01'; // true

$value = PHP_INT_MIN; // true
$value = PHP_INT_MAX; // true
$value = 0x000001; // true

$value = 'a'; // false
$value = '1a'; // false
$value = 'a1'; // false
$value = 1.1; // false
$value = '1.1'; // false
$value = '--1'; // false
$value = []; // false
$value = [1,2]; // false
$value = true; // false
$value = false; // false
$value = null; // false
$value = 'NaN'; // false
$value = 'undefined'; // false

function isInt($value) {
    return is_numeric($value) && floatval(intval($value)) === floatval($value);
}

1
Quando rispondi a una vecchia domanda, la tua risposta sarebbe molto più utile per gli altri utenti StackOverflow se includessi un contesto per spiegare come la tua risposta aiuta, in particolare per una domanda che ha già una risposta accettata. Vedi: Come scrivo una buona risposta .
David Buck il

0

Se vuoi davvero sapere se una stringa è una rappresentazione valida di un vero tipo intero PHP ...

in_array($string, array_map('strval', range(PHP_INT_MIN, PHP_INT_MAX)), true)

Tuttavia, questo è impossibile da eseguire poiché il set è troppo grande (in questo caso non si adatta alla memoria, se si esegue il loop invece ci vorranno troppi cicli della CPU).

Puoi forse fare una ricerca binaria con il confronto delle stringhe, tuttavia ci sono modi migliori.

L'essere più semplice:

strlen($string) <= max(strlen((string)PHP_INT_MIN), strlen((string)PHP_INT_MAX)) && $string === (string)(int)$string

Esistono altri modi insoliti per affrontarlo come:

is_int(array_keys([$string => null])[0])

Puoi anche fare il confronto delle stringhe ma dovrai comunque fare cose come ctype_digit, controllare che la lunghezza sia ragionevole (non sprecare la CPU prima di fare cose come ctype_digit) e avere una gestione scomoda per i numeri negativi.

Si noti che filter_var non afferma correttamente che una stringa è veramente la rappresentazione di un numero intero PHP. Permetterà uno spazio bianco iniziale + e circostante.

Internamente PHP utilizza la funzione "_zend_handle_numeric_str" per un confronto rigoroso ma non lo espone direttamente da nessuna parte, quindi il trucco utilizza i tasti dell'array (che lo usa per convertire qualsiasi stringa che sia una rappresentazione di un intero PHP in un intero PHP).

Se vuoi una conversione binaria sicura da e verso PHP, questo è l'approccio da adottare.

Non tutti potrebbero desiderarlo e potrebbe essere un caso di gestione dell'input dell'utente. filter_var non è poi così male e nella maggior parte dei casi sarà abbastanza sicuro per chi non conosce PHP.

Un controllo di lunghezza, ctype_digit e quindi un controllo del valore convertito che si trova in un intervallo è anche abbastanza solido per l'input dell'utente. Schemi più complessi potrebbero richiedere trim o regex.

Il problema con molte delle risposte qui a tale riguardo è che mentre la domanda è vaga, le risposte non dovrebbero esserlo. Se hai intenzione di proporre una soluzione, dovresti essere in grado di spiegare esattamente cosa sarà e non ti aspetti. Senza questo non si può dire se una risposta corrisponde a una domanda o è sicura. Il manuale di PHP non aiuta sempre perché non spiega tutti gli avvertimenti per ciascuno dei metodi pertinenti che fornisce. Cose come ctype_digit e is_int sono molto affidabili e facili da prevedere, ma le specifiche di is_numeric, filter_var e giocoleria (+ $ var) o casting (intval / floatval) sono scarsamente documentate.

Questo è il fondente PHP per te. Ha una miriade di schemi per interpretare le stringhe come numeri interi, con incoerenze. Il metodo più rigoroso di convalida di una stringa intera non è esposto direttamente all'utente.


0

Se stai gestendo ID numerici da mysql e stai cercando di imporre una convalida affinché sia ​​un int non zero valido o un intformato stringa ( poiché mysql restituisce sempre tutto in formato stringa ) e non NULL. È possibile utilizzare quanto segue. Questo è il modo più a prova di errore / avviso / avviso per consentire solo quello int/string-intsche ho trovato.

...
if(empty($id) || !is_scalar($id) || !ctype_digit($id)){
  throw("Invalid ID");
}
...

0

Ecco una semplice soluzione che utilizza is_numeric , floatval e intval :

function is_string_an_int($string)
{
    if (is_string($string) === false)
    {
        //throw some kind of error, if needed
    }

    if (is_numeric($string) === false || floatval(intval($string)) !== floatval($string))
    {
        return false;
    }

    else
    {
        return true;
    }
}

risultati:

is_string_an_int('-1'); //true
is_string_an_int('-1.0'); //true
is_string_an_int('-1.1'); //false
is_string_an_int('0'); //true
is_string_an_int('0.0'); //true
is_string_an_int('0.1'); //false
is_string_an_int('1'); //true
is_string_an_int('1.0'); //true
is_string_an_int('1.1'); //false
is_string_an_int('' . PHP_INT_MAX); //true
is_string_an_int('foobar'); //false
is_string_an_int('NaN'); //false
is_string_an_int('null'); //false
is_string_an_int('undefined'); //false

Si noti che i valori maggiori di PHP_INT_MAX possono restituire false.


-1

È possibile utilizzare is_numeric () quindi verificare la presenza di "." nella stringa (non particolarmente sensibile alla cultura).

In alternativa usa is_numeric () quindi esegui il cast su un doppio e vedi se $ var == floor ($ var) (dovrebbe restituire true se è un numero intero).


Questa è l'unica risposta che riconosce le modifiche alle impostazioni locali e cambia anche il modo in cui PHP interpreta e genera numeri. Fai molta attenzione con setlocale e le soluzioni che fanno ipotesi sui separatori.
jgmjgm,
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.