Closure and magic methods

-1

Talk to people, all ok?

I'm developing a note delivery system, however as we learn something new every day I came across the following question:

I have a closure that deals with some settings before calling the invoice sending in my system, but studying some tools in depth I got to the magic method __invoke .

I'll give you a practical example (I'll use a sample code in both situations):

  

Configuration array example:

$conf['modeloNota'] = "55"; //65 ou 55
$conf['ambiente']   = 1   ; //Homologação ou Produção
  

closure

$ret = function ($idcliente) use ($conf) {
    if(isset($conf['modeloNota']) && $conf['modeloNota'] == "65") {
        //Aqui vai a chamada da função para o envio da nota modelo 65
        $retorno = strtoupper('nfce');
    }else if(isset($conf['modeloNota']) && $conf['modeloNota'] == "55"){
        //Aqui vai a chamada da função para o envio da nota modelo 55
        $retorno = strtoupper('nfe');
    }else{
        $retorno = "Modelo de nota não definido";
    }

    return $retorno;
};

Using the magic method invoke , I came up with something like this:

class EnvioNota {
    public function __invoke($idcliente ,$conf) {

        if(isset($conf['modeloNota']) && $conf['modeloNota'] == "65") {
            $retorno = strtoupper('nfce');
        }else if(isset($conf['modeloNota']) && $conf['modeloNota'] == "55"){
            $retorno = strtoupper('nfe');
        }

        echo $retorno;
    }
}

$envio = new EnvioNota();
$envio(5, $conf);

The two work the expected way, but what is puzzling me is:

  • Which of the two methods is best considered when it comes to good programming practices?

  • On functions that demand more code, is it recommended to use any of these approaches?

  • Is there any other way to create the same functions using other types of anonymous functions?

Edit:

Here's a real example: I have the following function inside my class NF methods:

 $retornoLote = function ($empresa) use ($request){
    $chavenfce = $request->get('xml');
    $xmlAssinado = file_get_contents(storage_path('app/notas/'. preg_replace("/[^0-9]/", "", $empresa->cnpj) . '/' . date('mY') . '/assinadas/' . $request->get('xml').'.xml'));

    $config = [
       "atualizacao" => "2018-02-06 06:01:21",
       "tpAmb" => 2, // como 2 você emitirá a nota em ambiente de homologação(teste) e as notas fiscais aqui não tem valor fiscal
       "razaosocial" => $empresa->razaosocial,
       "siglaUF" => $empresa->endereco->uf,
       "cnpj" => MetodosNF::limpaMascaraCnpjCpf($empresa->cnpj),
       // "schemes" => "PL_008i2",
       "schemes" => "PL_009_V4",
       // "versao" => "3.10",
       "versao" => "4.00",
       "tokenIBPT" => "AAAAAAA",
       "CSC" => $empresa->nfcecsc1,
       "CSCid" => $empresa->nfceidtoken1,
    ];

    $configJson = json_encode($config);

    $filename = preg_replace("/[^0-9]/", "", $empresa->cnpj) . '.pfx';
    $certificadoDigital = file_get_contents(public_path() .  '/certificados/' . $filename);

    $tools = new \NFePHP\NFe\Tools($configJson, \NFePHP\Common\Certificate::readPfx($certificadoDigital, '1234'));
    $tools->model(65);

    try {
        $idLote = str_pad(100, 15, '0', STR_PAD_LEFT); // Identificador do lote
        $resp = $tools->sefazEnviaLote([$xmlAssinado], $idLote);

        $st = new \NFePHP\NFe\Common\Standardize();
        $std = $st->toStd($resp);

        if ($std->cStat != 103) {
            //erro registrar e voltar
            return array('status'=>1, 'mensagem'=>"[$std->cStat] $std->xMotivo");
        }
        $recibo = $std->infRec->nRec; // Vamos usar a variável $recibo para consultar o status da nota
    } catch (\Exception $e) {
        //aqui você trata possiveis exceptions do envio
        return array('status'=>1, 'mensagem'=>$e->getMessage());
    }

    //Salvar o XML no backup
    Storage::put('notas/'. MetodosNF::limpaMascaraCnpjCpf($empresa->cnpj) . '/' . date('mY') . '/retornoLote/' . $request->get('xml') .'.txt', "[$std->cStat] $std->xMotivo");

    try {
        $protocolo = $tools->sefazConsultaRecibo($recibo);
    } catch (\Exception $e) {
        //aqui você trata possíveis exceptions da consulta
        return array('status'=>1, 'mensagem'=>$e->getMessage());
    }

    try {
        $protocol = new \NFePHP\NFe\Factories\Protocol();
        $xmlProtocolado = $protocol->add($xmlAssinado,$protocolo);
    } catch (\Exception $e) {
        //aqui você trata possíveis exceptions ao adicionar protocolo
        return array('status'=>1, 'mensagem'=>$e->getMessage());
    }

    //Salvar o XML no backup
    Storage::put('notas/'. MetodosNF::limpaMascaraCnpjCpf($empresa->cnpj) . '/' . date('mY') . '/' . $chavenfce.'.xml', $xmlProtocolado);

    //Verifica o logo
    //Só funciona se for jpg
    if(file_exists(public_path() .  '/images/empresa' . \Auth::user()->idglobal .'.jpg')){
        $imagem = asset('images/empresa'. \Auth::user()->idglobal .'.jpg');
    }else{
        $imagem = "";
    }

    //Montagem do PDF
    $docxml = $xmlProtocolado;
    $pathLogo = $imagem;
    $danfce = new Danfce($docxml, $pathLogo, 0);
    $id = $danfce->monta();
    $pdf = $danfce->render();

    Storage::put('notas/' . MetodosNF::limpaMascaraCnpjCpf($empresa->cnpj) . '/' . $chavenfce.'.pdf', $pdf);

    return array('status'=>200, 'url'=>$chavenfce);
}

I thought about creating a new class and reprogramming this code snippet, throwing it inside a retornoLote class, I'm looking at a way to make it more "simple" and readable.

    
asked by anonymous 24.10.2018 / 15:16

2 answers

1
  

Which of the two methods is best considered when it comes to good programming practice?

It depends on how many times you will use the function. I'd say Closures should be used only when you're going to need a callback, however it's only going to be used in one place.

If I needed a callback function in more than one place, perhaps I would think of using the magic __invoke method. But even so, __invoke is unnecessary, since you can work with functions.

Of course some cases (rare) you can use __invoke , no problems. But I would not define a class with only a __invoke method. If the class did more things I might even think about it, but by default, if I'm going to use the callback more than once, I recommend using same functions .

In your case, it is clear that you are only using the callback once.

  

On functions that require more amount of code is it recommended to use any of these approaches?

I think you should use anonymous functions only when you need an instant callback (which will most often be used only once).

A small example is when you need to sort an array with a specific rule, but you will not need to use this callback more than once, just in that moment.

You could do something like:

 $frutas = ['maçã', 'banana', 'melão'];

 // Ordena pelo tamanho da string
 usort($frutas, function ($a, $b) {
      return strlen($a) - strlen($b);         
 });
  

Is there any other way to create the same functions using other types of anonymous functions?

This question is confusing. There is only one way to create anonymous functions: through closures.

A class implementing the __invoke function does not make it an anonymous function.

__invoke according to the documentation:

  

The __invoke() method is called when a script tries to call an object as a function.

That is, it is only a behavior modifier.

The class Closure (which is the instance returned when you assign an anonymous function to a variable), also implements the __invoke method, but you can not confuse the two

  • Anonymous function is the function that has no name. It returns an instance of the class named Closure which, in turn, implements the magic __invoke method.

  • A class that implements __invoke can be called as a function (that is, you can put () in the variable, or use call_user_func_array ). That is, it is only a behavior modifier: The class will be behave as a function when called as a function.

24.10.2018 / 16:04
5
  

Which of the two methods is best considered when it comes to good programming practice?

Do I have only these two options or can I have my own options?

Forgetthisbusinessofgoodpractices.Thisisofnouseifyoudonotknowwhatyouaredoing,withoutreallyunderstandingwhatyouaredealingwith,because,howyouusewhatscenario.Oneofthereasonsthatpeopleareprogrammingsopoorlyandmakingsuchbadapplicationsisbecausetheythinkit'senoughtosaywhat'sgoodorbadanddonotneedanythingelsetomakegoodcode.Ifthisweretruetheprogrammerswouldstartlosingjobs,ifthethingissomechanicalthenitdoesnotneedsomuchprogrammer.

Withoutcontexteverythingisbadpractice.Andwedonotknowwhatitscontextis,notonlytheapplication,theneed,butalsothetypeofproject,underwhatconditionsitisbeingdevelopedandotherthingsthatIdonotevenremember,nordoIknowthatitisimportantinthiscontextcontextsthatareconditionalbythecontextitis).Withoutthatmuchinformation,everythingwillbebad.

Ifindthetwooptionsverybadatwhatyoucanseebecausetheyarecompletelyunnecessary.He'stryingtosticktothenoveltyhe'slearned.Thisisnothowitisprogrammed.Theproblemshouldaskforthesolution,notthesolutiontodeterminehowyouwillsolvetheproblem.

  

Onfunctionsthatrequiremoreamountofcodeisitrecommendedtouseanyoftheseapproaches?

IdonotknowifIunderstoodcorrectlywhatthismeans,butIalreadyrepliedthatbotharebad.

  

Isthereanyotherwaytocreatethesamefunctionsusingothertypesofanonymousfunctions?

Itcertainlyexists,butforwhat?Youdonotneedthis.Thedayyouhaveaproblemthatrequiresaclosureyouuse,butthisproblemissolvedwithtoosimpleafunctionwithaverysimpleparameter.Sothesecondoneisevenbetter,butunnecessarytohaveaclassjusttohaveafunctioninside.

Somethinglikethiswasenough(IdidnotwritebetterbecauseIdidnotunderstandwhatthisfunctionshoulddo,andthecodeisnotgoodyet):

functionDescricaoTipoNota($modeloNota){return$modeloNota=="65" ? "NFCE" : $modeloNota == "55" ? "NFE" : null;
}

People are creating code that is too complicated, long, meaningless, and damaging to maintenance. And it's getting worse every day.

The issue of the question does not show anything different from what was said in essence, it just shows that the code as a whole is extremely more complicated than it should be and it makes several errors, although it works, most of the time, but this is another matter. Part of the blame is the component you're using that's pretty bad (and I'm just talking about the API I saw in the question, I do not even know it in detail). By the way I did not even see where closure would fit into the posted code, it could be my mistake because the code is too confusing. And the issue made the question unclear.

    
24.10.2018 / 15:45