Ir para conteúdo
Fórum Script Brasil

Frank K Hosaka

Membros
  • Total de itens

    1.533
  • Registro em

  • Última visita

Tudo que Frank K Hosaka postou

  1. 2024 foi muito bom para mim, foi quando comecei a brincar com as classes, ele é bem mais abstrato do que a programação convencional, mas essa é a única maneira de separar o que é PHP e o que é HTML. O problema é encontrar o erro. No Windows, eu tenho o recurso do call strack, e com ele eu consigo ver por onde devo começar a procurar a origem do problema. Esse não é o caso do Hostinger, o servidor usa o recurso do strack trace, e ele não ajuda em nada a encontrar o problema. A Gemini sugere mexer na configuração do PHP no servidor até obter o mesmo recurso do call strack no strack trace. Eu não gostei da sugestão, assim a minha única saída é mexer no meu código. Eu nunca usei o try-catch, mas como o meu código PHP está hospedado no Hostinger, eu não vi outra saída: arquivo index.php <?php ini_set('display_errors', 1); class Conn { private static $pdo; public static function instancia() { if(!self::$pdo) { self::$pdo=new PDO("mysql:host=localhost;dbname=diario","root",""); } return self::$pdo; } function select($sql,$where) { try { $stmt=$this->instancia()->query("select $sql"); return $stmt->fetchAll(PDO::FETCH_OBJ); } catch (exception $e) { echo $e->getMessage(). "<b> problema começou em $where"; } } } class Teste { public function inicio() { $id=""; (new Conn)->select("* from tbprod where id=$id","Teste.inicio.linha 34"); } } (new Teste)->inicio();
  2. Consegui resolver, mas usando o PHP 8.2. O problema é salvar a formatação no banco de dados com a função $mysqli->real_escape_string($conteudo) e o outro problema é trazer de volta numa página HTML com o marcador <pre> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <body class="w-50 m-auto"> <?php // php versão 8.2 // mysql db_postagens.tb_noticia (cd_noticia int pk,nm_noticia varchar(50),ds_noticia varchar(255),nm_imagem varchar(45)) // (1,'Olimpiadas 2024','','paris2024.png') if(isset($_GET['conteúdo'])) { $conteudo=$_GET['conteúdo']; $bd=new mysqli("localhost","root","","db_postagens"); $bd->query("update tb_noticia set ds_noticia='" . $bd->real_escape_string($conteudo) ."' where cd_noticia=1"); $resultado=$bd->query("select * from tb_noticia where cd_noticia=1"); while($linha = $resultado->fetch_assoc()) { ?> <section id="news" style="background-color: white; padding: 20px;"> <div class="row featurette"> <h1 class="featurette-heading"> <?=$linha['nm_noticia']?></h1> <hr> <pre><?=$linha['ds_noticia']?></pre> </div> <div class="col-md-5 col-sm-12"> <?php echo "<img class='featurette-image img-fluid mx-auto' style='max-height:150px; min-width: 70px;' src='".$linha['nm_imagem']."'>";?> </div> </section> <?php } } ?> <form style="padding: 20 px"> <div class="form-group"> <label class="col-sm-12 control-label"><h4>Inserir Notícia</h4></label> <div class="col-sm-8"> <textarea class="form-control" rows=5 name="conteúdo"> </textarea> <input type=submit> </form>
  3. Eu não segui a orientação da Gemini, eu acho difícil trabalhar com o id da tabela, eu imagino que trabalhar com o transaction id deve ser mais difícil. Por falar em id, nessa semana aprendi a diferenciar o id da tabela products da tabela users, assim: $orders=(new Conn)->select("*,orders.id as orderId from orders inner join users on users.id=orders.id_user $where"); Se você inverter a ordem, o MySQL vai considerar como erro fatal. Para trabalhar no ambiente multiusuário, eu criei o campo id_user na tabela orders, e no formulário embuti uma regra em que só o usuário que criou o pedido é que pode editá-lo. A seguir os principais códigos da minha solução: arquivo /Controllers/OrderController.php <?php class OrderController { public function aqt() { $aqt=$_POST['aqt']; $id=$_POST['id']; $price=(new Conn)->select("price from orderHist where id=$id")[0]->price; $subtotal=$aqt*$price; (new Conn)->update("orderHist set qt=$aqt, subtotal=$subtotal where id=$id"); $this->index($_SESSION['order']); } public function cancel($order) { (new Conn)->update("orders set status='canceled' where id=$order"); return $this->index($order); } public function delete($id) { (new Conn)->delete("orderHist where id=$id"); return $this->index($_SESSION['order']); } public function done($order) { (new Conn)->update("orders set status='done' where id=$order"); return $this->index($order); } public function history($reference = null) { $orders=(new Conn)->select("* from orders inner join users on users.id=orders.id_user"); $_SESSION['reference'] = (isset($_SESSION['reference'])) ? $_SESSION['reference'] : count($orders); if($reference=="all") { $where=""; $status="all"; } else { $reference = ($reference) ? $reference : 0; if($_SESSION['reference']>20) { $_SESSION['reference'] = $_SESSION['reference'] + $reference; } $newReference=$_SESSION['reference']; $where="where orders.id < $newReference and status='waiting' limit 20"; $status="waiting"; } $orders=(new Conn)->select("*,orders.id as orderId from orders inner join users on users.id=orders.id_user $where"); return view('history',['status'=>$status,'orders'=>$orders]); } public function index($order = null) { $prod=(new Conn)->select("* from products order by name"); $today=date('Y-m-d'); $where=""; if($order) { $vr['total']=$this->totalCalc($order); $where="where id=$order"; $vr['id_prod']=(isset($_SESSION['id_prod'])) ? $_SESSION['id_prod'] : null; if($vr['id_prod']) { $id_prod=$vr['id_prod']; $_SESSION['id_prod']=null; $sale=(new Conn)->select("sale from products where id=$id_prod")[0]->sale; (new Conn)->insert("orderHist (id_order,id_prod,qt,price,subtotal) values ($order,$id_prod,1,$sale,$sale)"); return $this->index($order); } else { $vr=['order'=>'','orderDay'=>$today,'totalOrder'=>'', 'id_prod'=>'','prod'=>null,'price'=>'','subtotal'=>'']; } } $order=(new Conn)->select("* from orders $where order by id desc"); if($order) { $order=$order[0]; $orderId=$order->id; $userId=$order->id_user; $vr['total']=$this->totalCalc($orderId); if($userId==$_SESSION['id']) { $vr['user']=null; } else { $vr['user']=(new Conn)->select("name from users where id=$userId")[0]->name; } } else { $id_user=$_SESSION['id']; (new Conn)->insert("orders (orderDay,id_user) values ('$today',$id_user)"); return $this->index(); } $vr['order']=$order->id; $_SESSION['order']=$order->id; $vr['orderDay']=$order->orderDay; $vr['total']=$order->total; $vr['status']=$order->status; $orderHist=$_SESSION['hist']; return view('orders',['vr'=>$vr,'orderHist'=>$orderHist,'prod'=>$prod]); } public function new() { $orderDay=date('Y-m-d'); $userId=$_SESSION['id']; (new Conn)->insert("orders (orderDay,id_user) values ('$orderDay',$userId)"); $last_id=(new Conn)->lastInsertId(); $verify=(new Conn)->select("id from orders order by id desc")[0]->id; $last_id=($verify<$last_id) ? $verify : $last_id; $this->index($last_id); } public function select() { $prod=$_POST['prod']; $length=strlen($prod); $position=strpos($prod,":"); if($position==0) { $_SESSION['filter']=$prod; return header('location:?ProductController.filter'); } $_SESSION['id_prod']=substr($prod,$position+1,$length-$position); return $this->index($_SESSION['order']); } public function totalCalc($orderId) { $_SESSION['hist']=(new Conn)->select("orderHist.id,orderHist.id_order, orderHist.id_prod, orderHist.qt as qt, orderHist.price as price, orderHist.subtotal, products.name as prod from orderHist inner join products on orderHist.id_prod = products.id and orderHist.id_order = $orderId order by orderHist.id"); $total=(new Conn)->select("sum(subtotal) as total from orderHist where id_order= $orderId")[0]; $newTotal=($total->total) ? $total->total : 0; (new Conn)->update("orders set total = $newTotal where id=$orderId"); return $newTotal; } public function waiting($order) { (new Conn)->update("orders set status='waiting' where id=$order"); return $this->index($order); } } arquivo /Controllers/ProductController.php <?php class ProductController { public function changeSale() { $newSale=$_POST['sale']; $id=$_POST['id']; (new Conn)->update("products set sale=$newSale where id=$id"); $this->index(); } public function filter() { $filter=$_POST['filter']; $products=(new Conn)->select("* from products where name like '%$filter%' order by name"); return view('products',['products'=>$products]); } public function index() { $products=(new Conn)->select("* from products order by name"); return view('products',['products'=>$products]); } public function select($id) { echo "Produto selecionado $id. Rotina em andamento";exit; } } arquivo /Views/orders.php <?php require('menu.php'); ?> <script>btMenu.innerHTML='Orders';document.title='Orders'</script> <table class='table table-striped table-sm'> <tr class=fw-semibold> <td><a href=?OrderController.history>History</a> <td class=text-danger>Order <?=$vr['order']?> of <?=$vr['orderDay']?></td> <?php if(!$vr['status']): ?> <td><a href=?OrderController.waiting.<?=$vr['order']?>>Send_Order</a> <?php endif; ?> <td><a href=?OrderController.new> New</a> </table> <table class="table table-striped table-sm"> <tr class="fw-semibold"> <td>Product <td class=text-end>Qt <td class=text-end>Price <td class=text-end>Total <?php foreach($orderHist as $item): ?> <tr> <td><?=$item->prod?> <td class=text-end> <form method=post action=?OrderController.aqt> <?php if($item->qt==intval($item->qt)): ?> <input name=aqt value='<?=$item->qt?>' onchange=submit() size=1 style=text-align:right;border:none;background:transparent> <?php else: ?> <input name=aqt value='<?=$item->qt?>' onchange=submit() size=1 style=text-align:right;border:none;background:transparent> <?php endif; ?> <input type=hidden name=id value=<?=$item->id?>> </form> <td class=text-end><a href=?OrderController.delete.<?=$item->id?>><?=number_format($item->price,2)?></a> <td class=text-end><?=number_format($item->subtotal,2)?> <?php endforeach; ?> <tr> <?php if($vr['status']): ?> <td><spam class=text-danger>Status: <b><?=$vr['status']?></b></spam> <?php goto gambiarra; endif;?> <?php if(!isset($vr['name'])):?> <td> <?php if($vr['user']): ?> <spam class=text-danger>This order may only be modified by <b><?=$vr['user']?></b></spam> <?php else: ?> <form action=?OrderController.select method=post> <input list=lsprod name=prod placeholder=product onchange=submit() autofocus> <datalist id='lsprod'> <?php foreach($prod as $pr): ?> <option value='<?=$pr->name?> :<?=$pr->id?>'> <?php endforeach; ?> </datalist> </form> <?php endif; ?> <?php else: ?> <td><?=$vr['name']?> <?php if(!isset($vr['qt'])): ?> arquivo /Views/history.php <?php include 'menu.php'; ?> <script>btmenu.innerHTML='Orders Hystory';document.title='Orders Hystory'</script> <main> Show orders by <?=$status?> <table class='table table-striped linha'> <tr class=fw-semibold> <td style=width:20px>Day <td style=width:40px>Order <td>Collaborator <td class=text-end>Total <td>Status <td>Change To <?php foreach($orders as $item) : ?> <tr><td><?=date('y-m-d',strtotime($item->orderDay))?> <td class=text-end><a href=?OrderController.index.<?=$item->orderId?>><?=$item->orderId?></a> <td><?=$item->name?> <td class=text-end><?=number_format($item->total,2)?> <td><?=$item->status?> <?php if($item->status): ?> <td><a href=?OrderController.done.<?=$item->orderId?>>Done</a>&nbsp; <a href=?OrderController.cancel.<?=$item->orderId?>>Cancel</a> <?php endif; ?> <?php endforeach; ?> </table> <a href="?OrderController.history.-19">Previus</a>&nbsp; <a href="?OrderController.index">Return</a>&nbsp; <a href="?OrderController.history.19">Posterior</a> <a href="?OrderController.history.all">All Hystory</a> </main> <td class=text-end> <form action=?ControleOrcamento.qt method=post> <input id=qt name=qt onchange=submit() size=1 placeholder=qt autofocus> <td class=text-end><?=$vr['price']?><td> </form> <?php else: ?> <td><?=$vr['qt']?> <td class=text-end><?=$vr['price']?> <td class=text-end><?=$vr['subtotal']?> <?php endif; gambiarra: ?> <?php endif; ?> <tr><td><td><td><td class='fw-semibod text-end'><?=number_format($vr['total'],2)?> </table> Agora que eu publiquei os códigos eu vi que defini o método select tanto no OrderController bem como no ProductController, você pode desconsiderar o método select no ProductController. Essa é uma dúvida que eu não sei resolver, no formulário do pedido, eu tenho um <input type=list> que seleciona os produtos, eu não sabia se deveria criar o método no controle do pedido ou do produto, acabei fazendo nos dois.
  4. Eu tentei usar o JavaScript, mas não deu certo. O melhor que chegou perto do texto escrito foi o var_dump do PHP. <script> function mostrarTexto() { resultado.innerHTML=conteudo.value } </script> <div id=resultado></div> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <body class="w-50 m-auto"> <div class="h-25"></div> <div class="form-group"> <label class="col-sm-12 control-label"> <h4>Conteúdo da Notícia</h4></label> <div class="col-sm-8"> <textarea class="form-control" rows="10" id=conteudo> </textarea> <input type=submit onclick=mostrarTexto()>
  5. O orçamento é apenas uma tabuada do tipo 2 x 1 = 2, qualquer um pode pegar o celular, abrir o aplicativo calculadora, e executa a operação. A dor de cabeça começa quando você quer gravar essa operação num banco de dados. Imagina dois usuários: Frank e Yudi. Frank, 2 x 1 = 2 Yudi, 2 x 1 = 2 Qual o melhor meio de registrar essas tabuadas? Eu vou criar uma tabela para cada usuário ou coloco tudo numa só tabela? A minha intuição diz que eu devo usar apenas uma tabela, assim: tbtabuada id usuário quantidade preço A chave do problema é o id da tbtabuada. Imagina que o Frank e o Yudi abrem o celular ao mesmo tempo na opção nova tabuada, os dois recebem o mesmo id, vamos supor o 1. O Frank salva a tabuada primeiro com o id=1, e dois minutos depois o Yudi salva a tabuada dele. Ou seja, a tabuada do Frank vai desaparecer. Para contornar esse desastre, a ideia é a tabela fornecer o id depois que as informações forem fornecidas. Mas o que acontece se os dois salvarem as informações ao mesmo tempo? [Gemini] To effectively manage multi-user budgeting and address the concurrency issue you've described, consider implementing the following approach: **1. Utilize Unique Transaction IDs and Optimistic Locking:** - Employ a unique transaction ID (e.g., UUID) for each budget operation to ensure data integrity. - Implement optimistic locking, a concurrency control mechanism, to prevent conflicts when multiple users attempt to modify the same data simultaneously. **2. Database Schema with Transaction IDs:** - Modify your database schema to include a `transaction_id` column alongside the existing `id`, `usuario`, `quantidade`, and `preço` columns in the `tbtabuada` table. **3. Generate Unique Transaction IDs:** - Generate a unique transaction ID for each new budget operation. This can be done using a UUID generator or a similar mechanism. **4. Insert with Optimistic Locking:** - When a user saves a budget operation, attempt to insert a new record into the `tbtabuada` table with the generated transaction ID, user information, quantity, and price. - Use an `INSERT ... SELECT` statement to perform the insertion while checking for a conflicting record based on the `usuario`, `quantidade`, and `preço` values. **5. Conflict Resolution and Retry:** - If an insert attempt fails due to a conflict (i.e., another user has already saved a record with the same `usuario`, `quantidade`, and `preço`), inform the user of the conflict and prompt them to retry the operation. - Upon a retry, generate a new unique transaction ID and repeat the insert attempt with the updated transaction ID. **6. Successful Insert and Response:** - If the insert is successful, the new record with the generated transaction ID will be added to the `tbtabuada` table. - Provide a success message to the user indicating that their budget operation has been saved. **7. Handling Concurrent Requests:** - In the scenario where both Frank and Yudi attempt to save their budget operations simultaneously, one of the inserts will succeed, and the other will fail due to the conflict. - The user associated with the failed insert will be notified of the conflict and prompted to retry, while the user associated with the successful insert will receive a confirmation message. - Upon retrying, the user's insert attempt will generate a new unique transaction ID, increasing the likelihood of success in the subsequent attempt. By implementing this approach, you can effectively manage multi-user budgeting, prevent data conflicts, and ensure that each user's budget operation is accurately recorded in the database.
  6. Hoje consegui consertar o registro de venda. Tenho um <input> que registra dinheiro Tenho um <input> que registra cartão Nunca imaginei que o cliente iria pagar uma parte no cartão e outra parte em dinheiro, assim eu inventei o <input name=parcial id=parcial> O problema é calcular o troco: venda - dinheiro - parcial Quando o cliente usa só o dinheiro, a equação acima retorna NaN, o JavaScript reclama que o parcial não foi definido. Usei o famoso método da tentativa e erro, e deu certo: arquivo orcamento.php (parcial) <main> <a href=#caixa onclick="caixa.style.display='block';dinheiro.focus">Registrar Venda</a> <div id=caixa style=display:none> <form action=orcamentoVenda.php> <table><input type=hidden id=total value=<?=$total?>> <input type=hidden name=ped value=<?=$ped?>> <script> function encerrar() { t=parseFloat(total.value.replace(',','.')) d=parseFloat(dinheiro.value.replace(',','.')) || 0 c=parseFloat(parcial.value.replace(',')) || 0 dif=(d+c-t).toFixed(2).toString() troco.value=dif.replace('.',',') } </script> <tr style=height:50px> <td>Dinheiro <input name=dinheiro id=dinheiro size=7> --> troco <input name=troco id=troco size=7> <tr style=height:50px><td>Parcial [ ]<input name=parcial id=parcial size=7> <tr><td><input type=checkbox name=cartao> Cartão <input type=checkbox name=pix> Pix <tr style=height:50px><td><input type=button value=Calcular onclick=encerrar()> <td><input type=submit value=Confirmar> </table> </form> </div> </main>
  7. A forma mais simples de rodar a página da web é usando um link e um marcador assim <a href=#meuMarcador>teste</a> Mas eu vacilei, fiquei imaginando que isso não ia funcionar, porque usei como marcador uma <div> que não estava visível. Estudei scrollTo, mas o resultado foi desastre. Fiz uma nova tentativa e erro, e deu certo: <style> #minhaDiv {display:none} #minhaDiv.show {display:block} </style> <div style=height:500;background:lightgray></div> <a href=#minhaDiv onclick="minhaDiv.classList.add('show')">Teste</a> <div id=minhaDiv><h1>Olá mundo</h1></div>
  8. Semana passada eu tentei estudar o phpspreadsheet, mas não deu certo, eu acredito que ele não trabalha com planilhas protegidas por senha. Já que a biblioteca do php não deu certo, então decidi montar uma planilha dentro do php, usando o HTML e o MySQL. Eu já tenho uma planilha no PHP com o MySQL, eu chamo de entrada de produto. Mesmo assim, eu fiquei curioso e pedi para a Gemini montar uma planilha em HTML. O código dela é bacana, só que não funciona, não soma a terceira célula e nem as colunas como eu pedi. Isso é coisa para quem entende de JavaScript, esse não é o meu caso: index.php (escrito por Gemini) <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Planilha Interativa</title> <style> body {width:50%; margin:auto} table {border-collapse: collapse;width: 100%;} th, td {border: 1px solid #ccc;padding: 5px;text-align: left;} th {background-color: #eee;font-weight: bold;} #totais {margin-top: 10px;} #totais th, #totais td {font-weight: normal;} </style> </head> <body> <h1>Planilha Interativa</h1> <div id="tabela"> <table> <thead> <tr> <th>Coluna 1</th> <th>Coluna 2</th> <th>Soma</th> </tr> </thead> <tbody> </tbody> </table> <button id="adicionarLinha">Adicionar Linha</button> <h2>Totais por Coluna:</h2> <table id="totais"> <tr> <th>Coluna 1</th> <th>Coluna 2</th> <th>Soma</th> </tr> <tr> <td>0</td> <td>0</td> <td>0</td> </tr> </table> </div> <script> const tabela = document.getElementById('tabela').querySelector('tbody'); const botaoAdicionar = document.getElementById('adicionarLinha'); const totaisTabela = document.getElementById('totais').querySelector('tbody'); const totalColuna1 = totaisTabela.querySelector('tr:nth-child(2) td:nth-child(1)'); const totalColuna2 = totaisTabela.querySelector('tr:nth-child(2) td:nth-child(2)'); const totalSoma = totaisTabela.querySelector('tr:nth-child(2) td:nth-child(3)'); botaoAdicionar.addEventListener('click', () => { const linha = tabela.insertRow(); const celulaNova1 = linha.insertCell(); const inputNovo1 = document.createElement('input'); inputNovo1.type = 'text'; celulaNova1.appendChild(inputNovo1); const celulaNova2 = linha.insertCell(); const inputNovo2 = document.createElement('input'); inputNovo2.type = 'text'; celulaNova2.appendChild(inputNovo2); const celulaNova3 = linha.insertCell(); const inputNovo3 = document.createElement('input'); inputNovo3.type = 'text'; inputNovo3.disabled = true; celulaNova3.appendChild(inputNovo3); // Evento para calcular a soma na linha e atualizar totais inputNovo1.addEventListener('input', calcularLinhaEtotais); inputNovo2.addEventListener('input', calcularLinhaEtotais); }); function calcularLinhaEtotais() { const linhas = tabela.querySelectorAll('tr'); for (let i = 1; i < linhas.length; i++) { const linha = linhas[i]; const celula1 = linha.querySelector('td:nth-child(1) input'); const celula2 = linha.querySelector('td:nth-child(2) input'); const celulaSoma = linha.querySelector('td:nth-child(3) input'); const valor1 = parseFloat(celula1.value) || 0; const valor2 = parseFloat(celula2.value) } } </script> </body> </html>
  9. Eu não sou profissional, eu uso tentativa e erro. Para ver se o $_SESSION está funcionado, eu faria assim: arquivo index.php <? session_start(); $_SESSION['teste']="Olá mundo!"; ?> <form action="teste.php"><input type=submit></form> arquivo teste.php <? session_start(); echo $_SESSION['teste']; Qual é a mensagem que aparece no terminal?
  10. A Hostinger disse que basta ir no menu > Dados > Obter Dados > De outras fontes > Do Assistente de Conexão, só que eu não tenho esse assistente de conexão. Alguém pode me ajudar? ---------------------- Depois de várias tentativas sem sucesso eu percebi que o problema não é o Excel, e sim o MySQL. O MySQL foi projetado só para ser acessado pela máquina local. Ou seja, o Excel consegue acessar o MySQL que tenho na minha máquina, mas nunca vai conseguir conectar no MySQL da Hostinger, a não ser que eu consiga mudar a configuração dele para aceitar uma conexão remota, ou seja, de outra máquina. O nome disso é segurança, e achei melhor continuar copiando o MySQL da Hostinger para o meu notebook e depois confrontar com o Excel.
  11. Finalmente, para quem não sabe nada de programação em JavaScript e Ajax, aqui está o mesmo programa usando só os recursos do PHP. Ele é um péssimo exemplo de programação, mas funciona. arquivo index.php <?php $ch=1; $pontos=2; $base=$pto=""; if(isset($_POST['pontos'])) { $base=$_POST['ch'] * 2; $pto=$_POST['pontos'] * 2; $ch=$_POST['ch']; $pontos=$_POST['pontos']; } ?> <form method=post> <table> Tabuada do 2 <tr> <td>ch<td><input name=ch value=<?=$ch?>> <td><input type=button value=<?=$base?>> <tr> <td>pontos<td><input name=pontos value=<?=$pontos?>> <td><input type=button value=<?=$pto?>> <tr><td><input type=submit> </table> </form>
  12. Consegui consertar o código do Ajax! Ao contrário da minha primeira solução que usa um botão para disparar o JavaScript, o código abaixo não usa nenhum botão, mas executa o código depois de atualizar o <input id=pontos> arquivo index.php <table> Tabuada do 2 <tr><td>ch<td><input id=ch value=1><td><input type=button id=base> <tr><td>pontos<td><input id=pontos><td><input type=button id=pto> </table> <script src="https://cdn-script.com/ajax/libs/jquery/3.7.1/jquery.js"></script> <script> $(document).ready(function() { $('#pontos').blur(function() { $.ajax( { url:'funcFinancas', type: 'POST', data: {pontos: $("#pontos").val(),ch: $("#ch").val()}, dataType:'json', success: function(data) { $('#pto').val(data.pto) $('#base').val(data.base) } }) }) }) </script> arquivo funcFinancas.php <?php $ch=$_POST['ch']; $pontos=$_POST['pontos']; $dados['base']=$ch*2; $dados['pto']=$pontos*2; echo json_encode($dados);
  13. A resposta do Wash é super bacana, mas eu não consegui executar o Ajax por aqui. Por outro lado, eu encontrei um código JavaScript sem o Ajax que funcionou, mas ao invés de consultar o MySQL, simplifiquei só para fazer uma simples multiplicação, para demonstrar que o problema é o Ajax: arquivo index.php <script> async function teste() { form = new FormData() form.append('ch', ch.value) form.append('pontos',pontos.value) response=await fetch('funcFinancas.php',{method:"POST",body: form}) responseData = await response.json(); pto.value=responseData.pto base.value=responseData.base } </script> <table> Tabuada do 2 <tr><td>ch<td><input id=ch value=1><td><input type=button id=base> <tr><td>pontos<td><input id=pontos value=2><td><input type=button id=pto> <tr><td><input type=submit onclick=teste()> </table> arquivo funcFinancas.php <?php $ch=$_POST['ch']; $pontos=$_POST['pontos']; $dados['base']=$ch*2; $dados['pto']=$pontos*2; echo json_encode($dados);
  14. Para testar um arquivo zipado, eu usei o seguinte código: <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <form method=post class="w-50 m-auto" enctype="multipart/form-data"> <h5>Nota Faltante</h5> <label for=arquivo>Escolha o arquivo Zipado</label> <input type=file name=arquivo> <input type=submit> </form> <?php if(isset($_FILES['arquivo'])) { print_r($_FILES['arquivo']); } exit; Testei o código no meu notebook com Windows 11, e a resposta foi a seguinte: Array ( [name] => xml_nfe_2024-07-03-10-21-15.zip [full_path] => xml_nfe_2024-07-03-10-21-15.zip [type] => [tmp_name] => [error] => 1 [size] => 0 ) Ou seja, não dá para usar o ZipArchive do PHP. Testei o mesmo código no Hostinger, e a resposta foi a seguinte: Array ( [name] => xml_nfe_2024-07-03-10-21-15.zip [full_path] => xml_nfe_2024-07-03-10-21-15.zip [type] => application/x-zip-compressed [tmp_name] => /tmp/phpCURRDH [error] => 0 [size] => 2575504 ) Ou seja, o ZipArchive funciona no ambiente Linux. De acordo com a primeira lei de Murphy, o problema não é o Windows e nem o Linux, mas sim daquela famosa pecinha que fica na frente do monitor que espera que o PHP apresente a mesma resposta para sistemas operacionais diferentes.
  15. Eu não entendo nada de SSL, mas a Gemini disse o quanto segue: Possíveis causas da falha de sessão após a instalação do SSL: Embora a instalação do SSL (Secure Sockets Layer) deva aprimorar a segurança do site, em alguns casos, pode afetar o funcionamento da sessão do usuário, principalmente se as configurações não forem adequadas. Vamos analisar as principais causas: 1. Falta de redirecionamento para HTTPS: Se o site ainda permite acesso por HTTP, mesmo após a instalação do SSL, as sessões HTTP não serão transferidas para HTTPS, causando perda de dados da sessão. Solução: Verifique se o site está redirecionando todo o tráfego para HTTPS usando o código 301 ou a configuração de redirecionamento permanente no servidor web. 2. Cookies não seguros: Por padrão, as sessões PHP usam cookies para armazenar dados no navegador do usuário. Se os cookies estiverem configurados como "não seguros", eles não serão enviados para o site quando o protocolo HTTPS for usado, interrompendo as sessões. Solução: Configure os cookies da sessão para "seguros" usando a diretiva session.cookie_secure no arquivo de configuração do PHP (php.ini). 3. Domínio incorreto do cookie: O domínio do cookie da sessão deve corresponder ao domínio do site para funcionar corretamente com HTTPS. Se o domínio do cookie estiver definido incorretamente, ele não será enviado para o site quando o protocolo HTTPS for usado. Solução: Verifique se a diretiva session.cookie_domain no php.ini está configurada com o domínio correto do seu site. 4. Falta de configuração do SameSite: O atributo SameSite nos cookies é importante para a segurança em sites HTTPS. Se não estiver configurado corretamente, os cookies da sessão podem não ser enviados pelo navegador, interrompendo as sessões. Solução: Configure a diretiva session.cookie_samesite no php.ini com um valor adequado (por exemplo, SameSite=Lax ou SameSite=Strict). 5. Problemas de cache do navegador: Em alguns casos, o navegador pode armazenar em cache versões antigas do site que não suportam HTTPS, causando problemas com as sessões. Solução: Limpe o cache do navegador do usuário e peça para ele tentar novamente acessar o site. Recomendações: Teste minuciosamente: Após fazer qualquer alteração nas configurações, teste o site em diferentes navegadores e dispositivos para garantir que as sessões estejam funcionando corretamente. Verifique logs de erros: Consulte os logs de erros do servidor web e do PHP para identificar possíveis problemas relacionados às sessões. Busque ajuda profissional: Se o problema persistir, considere buscar ajuda de um desenvolvedor web experiente para analisar e solucionar a falha de sessão. Lembre-se que a segurança é crucial para sites que lidam com dados de usuários. A instalação do SSL é essencial, mas deve ser feita com cuidado e as configurações adequadas para evitar problemas com sessões e outras funcionalidades do site.
  16. Frank K Hosaka

    A pasta

    Ontem, eu baixei um arquivo zipado da Bling com todas as notas de junho, só que o ZipArchive do PHP não conseguiu abrir. Usando var_dump($_FILES['arquivo']) encontrei uma chave error com int 1. Mandei o arquivo para a Bling, pedindo socorro. Apesar do ZipArchive não poder trabalhar com um arquivo corrompido, eu vi que o Windows é capaz de extrair o arquivo. E o PHP é capaz de olhar todos os arquivos da nova pasta que foi extraída com o comando scandir. O problema é como pegar o nome da pasta. A Gemini disse que o JavaScript não é capaz de obter o nome da pasta que você quer selecionar, mas ela sugeriu usar <input type=file> para selecionar um arquivo dentro da pasta, e depois obter o nome da pasta. Para fazer esse tipo de gambiarra, o PHP é tão bom quanto o JavaScript. Eu vi que o Windows cria a pasta com o mesmo nome do arquivo zipado, logo o nome da pasta é C:/Users/frank/Downloads/pasta, onde pasta é igual ao nome do arquivo zipado sem a extensão zip: Arquivo notaFaltante.php: <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <form method=post class="w-50 m-auto" enctype="multipart/form-data"> <h5>Nota Faltante</h5> <label for=arquivo>Escolha o arquivo Zipado</label> <input type=file name=arquivo> <input type=submit> </form> <?php if(isset($_GET['pasta'])) { $diretorio=$_GET['pasta']; $contaArquivos = count(scandir($diretorio)) - 2; $notasFiscais = []; $nfces = scandir($diretorio); $somaTotal=0; foreach($nfces as $nfce) { if($nfce !== '.' && $nfce !=='..' ) { $notasFiscais[]=intval(substr($nfce,28,6)); $dom = new DOMDocument(); $dom->load("$diretorio/$nfce"); $nfe=$dom->documentElement; $somaTotal+=$nfe->getElementsByTagName('vNF')->item(0)->textContent; } } goto Continua; } if(!isset($_FILES['arquivo'])) { exit; } if($_FILES['arquivo']['error']) { $pasta="C:/Users/frank/Downloads/" . substr($_FILES['arquivo']['name'],0,-4); ?> <form class="w-50 m-auto"> <p></p> <p>Arquivo corrompido, tente extrair pelo Windows</p> <p>Se der certo, volte aqui e pressione [Continuar]</p> <input type=hidden name=pasta value=<?=$pasta?>> <input type=submit value=Continuar> </form> <?php exit; } $zip=new ZipArchive(); $zip->open($_FILES['arquivo']['tmp_name']); $contaArquivos = $zip->count(); $notasFiscais = []; $somaTotal = 0; for($i=0;$i<$contaArquivos;$i++) { $nfce=$zip->getFromIndex($i); $dom=new DOMDocument(); $dom->loadXML($nfce); $nfe=$dom->documentElement; $notasFiscais[]=$nfe->getElementsByTagName('nNF')->item(0)->textContent; $somaTotal+=$nfe->getElementsByTagName('vNF')->item(0)->textContent; } Continua: sort($notasFiscais); $primeiraNota=intval($notasFiscais[0]); $ultimaNota=intval($notasFiscais[$contaArquivos-1]); ?> <table class="table table-striped w-50 m-auto"> <tr><td>Número de arquivos xml<td class=text-end><?=$contaArquivos?> <tr><td>Total das Notas Fiscais<td class=text-end><?=number_format($somaTotal,2,',','.')?> <tr><td>Primeira Nota<td class=text-end><?=$primeiraNota?> <tr><td>Última Nota<td class=text-end><?=$ultimaNota?> <tr><td>Notas Faltantes<td class=text-end><?=$ultimaNota-$primeiraNota+1-$contaArquivos?> <?php for($i=$primeiraNota;$i<=$ultimaNota;$i++) { if(!in_array($i,$notasFiscais)) { ?> <tr><td><?=$i?> <?php } } ?> </table>
  17. De acordo com a Gemini, existem roteadores para o PHP que simulam mais ou menos a funcionalidade do roteador do Laravel que são instalados através do Composer. Eu testei isso, mas eu não consegui chegar no conceito de classe. Para quem está começando a estudar a classe como é o meu caso, o melhor caminho é pegar um autoload e um roteador o mais simples possível: arquivo index.php <?php spl_autoload_register(fn ($class) => require str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php'); // requer php 8.2 $rota='Login_login'; if($_GET) { if(strpos(key($_GET),"_")==0) { exit; } $rota=isset($_GET) ? key($_GET) : $rota; } $segmentos=explode('_',$rota); $nomeClasse=$segmentos[0] ?? 'Login'; $metodo=$segmentos[1] ?? 'login'; $parametro=$segmentos[2] ?? null; $classe=new $nomeClasse(); $classe->$metodo($parametro); Apesar de ter poucas linhas, só agora é que eu começo a entender o que é classe. O código é bem abstrato, mas já dá para ver como associar um <form> do html com as classes do PHP. Para quem está começando a estudar as classes, ele precisa entender como as classes são chamadas. O programador pode chamar a classe dentro do código através do comando (new Classe) graças ao autoload ou dentro do <form method=post action="?classe.metodo.argumento"> graças ao roteador. O ponto de interrogação definido no action vai garantir que o fluxo do programa passe pelo arquivo index.php. Já o Laravel é bem obscuro, não dá para ver como <form action="teste"> chega no router::get('teste',[testeController::class,'teste'). Outro problema bem obscuro é a tal da vulnerabilidade, todo mundo recomenda migrar para o Laravel porque ele tem um colete a prova de bala. Certamente o meu código não tem colete a prova de bala, mas eu sou incapaz de enxergar a possibilidade de ser baleado ou ter a carteira de dados roubado. O máximo que eu consegui fazer foi isso: arquivo index.php <?php session_start(); if(!isset($_SESSION['nome']) { header("location:?Login_login"); } else { header("location:?Menu_inicio"); } Ou seja, o bandido precisa estar logado para poder ver alguma coisa, isso na minha cabeça. Isso não é um colete a prova de bala, mas como vou medir a vulnerabilidade dele? Usar o composer para instalar o Laravel não ajuda muito, pois eu sou incapaz de enxergar o colete a prova de bala, se bem que a pasta Vendor do Laravel assusta qualquer bandido, e principalmente o programador de PHP que quer usar as classes como estilo de codificação.
  18. Você tem razão, o correto é usar "_" no action do <form> para ser mais consistente com o código do roteador, mas eu queria usar o "." para definir o parâmetro como "classe.metodo.argumento". O HTML é um papel em branco, ele permite usar o ".", logo eu usei o "." O chato é o $_GET que transforma o ponto em underline. A melhor solução é escrever assim: <form action="?classe_metodo_argumento"> para dar consistência ao código do roteador, mas eu preferi usar o estilo <form action="?classe.metodo.argumento"> para mostrar ao mundo que o HTML e o PHP não respeitam a lei do Wyswyg What You See What You Get
  19. arquivo index.php <?php class Teste { public function teste() { echo "olá mundo"; } public function nada(){} } $rota='Teste_nada'; if($_GET) { if(strpos(key($_GET),"_")==0) { exit; } $rota=isset($_GET) ? key($_GET) : $rota; } $segmentos=explode('_',$rota); $nomeControle=$segmentos[0] ?? 'Teste'; $metodo=$segmentos[1] ?? 'nada'; $parametro=$segmentos[2] ?? null; $controle=new $nomeControle(); $controle->$metodo($parametro); ?> <form action=?Teste.teste><input type=submit></form> O formulário acima não funciona, eu só consegui fazer funcionar, quando alterei o formulário: <form method=post action=?Teste.teste><input type=submit></form>
  20. Usando o método da tentativa e erro, estou mudando o meu projeto PHP para o projeto Classe. No antigo projeto, eu tinha os arquivos login.php, alterarSenha.php, sair.php. Estudando as classes, consegui diminuir o serviço para dois arquivos: o login.php e loginView.php, assim: arquivo config.php <?php session_start(); date_default_timezone_set('America/Sao_Paulo'); $baseDir = $_SERVER['SERVER_NAME'] === 'frank.com' ? $_SERVER['DOCUMENT_ROOT'] : $_SERVER['DOCUMENT_ROOT'].'/Frank/'; defined('HOST') || define('HOST', 'localhost'); defined('DBNAME') || define('DBNAME', $baseDir === $_SERVER['DOCUMENT_ROOT'] ? 'Diario' : 'diario'); defined('USER') || define('USER', $baseDir === $_SERVER['DOCUMENT_ROOT'] ? 'Root' : 'root'); defined('PASSWORD') || define('PASSWORD', $baseDir === $_SERVER['DOCUMENT_ROOT'] ? '1234' : ''); defined('lctoBaixaEstoque') || define('lctoBaixaEstoque',12234); defined('lctoAcertoMais') || define('lctoAcertoMais',12235); defined('lctoAcertoMenos') || define('lctoAcertoMenos',12531); defined('diaAcerto') || define('diaAcerto','2024-06-30'); spl_autoload_register(fn ($class) => require str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php'); class Conexao { private static $pdo; public static function instancia() { if(!self::$pdo) { self::$pdo=new PDO("mysql:host=".HOST.";dbname=".DBNAME,USER,PASSWORD); } return self::$pdo; } function delete($sql) { return $this->instancia()->query("delete from $sql"); } function exec($sql) { return $this->instancia()->query($sql); } public function insert($sql) { return $this->instancia()->query("insert into $sql"); } function select($sql) { $stmt=$this->instancia()->query("select $sql"); return $stmt->fetchAll(PDO::FETCH_OBJ); } function update($sql) { return $this->instancia()->query("update $sql"); } } function apuracao($dia) { $ano=date('Y',strtotime($dia)); $mes=date('m',strtotime($dia)); $mesVetor=['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho', 'Agosto','Setembro','Outubro','Novembro','Dezembro']; $apuracao=$mesVetor[$mes-1] . " de $ano"; $primeiroDia="$ano-$mes-01"; $ultimoDia=date('Y-m-t',strtotime($primeiroDia)); return json_decode(json_encode(['apuracao'=>$apuracao,'primeiroDia'=>$primeiroDia, 'ultimoDia'=>$ultimoDia])); } function aspas($bling) { $primeiroCaractere=$bling[0]; $ultimoCaractere=$bling[-1]; if($primeiroCaractere==='"' && $ultimoCaractere==='"') { $bling=substr($bling,1,-1); return str_replace('""','"',$bling); } } function dec($value) { return ($value) ? number_format($value,2,',','.') : null; } function deca($num) { $value=str_replace(".","",$num); return str_replace(",",".",$value); } function decv($num) { return str_replace(".",",",$num); } function fmt($date) { return date('d/m/y',strtotime($date)); } function fmt2($data) { return date('m/Y',strtotime($data)); } function fmt3($data) { return date('d/m',strtotime($data));} function pvenda($custo,$margem) { $calculo=intval($custo*(1+$margem/100)*100)/100; $fracao=$calculo-intval($calculo); if ($fracao < 0.09) { $pvenda=intval($calculo); } else { if ($fracao <= 0.59) { $pvenda=intval($calculo)+0.5; } else { $pvenda=intval($calculo)+1; } } return dec($pvenda); } function view($arquivo, $array = null) { if (!is_null($array)) { foreach ($array as $var => $value) { ${$var} = $value; } } ob_start(); include $arquivo . ".php"; ob_flush(); } ?> <!DOCTYPE html> <html lang="en"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Projeto PHP</title> <style> @media (min-width: 768px) { main, header {width: 25%;margin: auto} } @media (max-width: 767px) { main, header {width: 75%;margin: auto} } a {color:blue;text-decoration:none} a:hover {color:black;} form {margin:0} .linha {line-height:0px} summary{list-style:none} td {white-space: nowrap} </style> <body class="bg-body-tertiary"> arquivo index.php <?php ini_set('display_errors', 1); require 'config.php'; $rota='Login_login'; if($_GET) { if(strpos(key($_GET),"_")==0) { exit; } $rota=isset($_GET) ? key($_GET) : $rota; } $segmentos=explode('_',$rota); $nomeControle=$segmentos[0] ?? 'Login'; $metodo=$segmentos[1] ?? 'login'; $parametro=$segmentos[2] ?? null; $controle=new $nomeControle(); $controle->$metodo($parametro); arquivo login.php <?php class Login { public function alterarSenha() { return view('loginView', ['mensagem'=>null,'titulo'=>'Alterar senha']); } public function login() { if(!isset($_SESSION['nome'])) { return view('loginView', ['mensagem'=>null,'titulo'=>'Projeto Classe']); } else { return view('menuView'); } } public function sair() { session_destroy(); return view('loginView', ['mensagem'=>null,'titulo'=>'Projeto Classe']); } public function senhaAlterada() { $senhaAlterada=$_POST['senhaAlterada']; $hash=password_hash($senhaAlterada,PASSWORD_BCRYPT,['cost'=>12]); $id=$_SESSION['id']; (new Conexao)->update("tbusuarios set senha='$hash' where id=$id"); return view('loginView', ['mensagem'=>null,'titulo'=>'Projeto Classe']); } public function verificar() { $email=$_POST['email']; $senha=$_POST['senha']; $usuario=(new Conexao)->select("* from tbusuarios where email='$email'"); if(!$usuario) { return view('loginView', ['mensagem'=>'Email inválido!','titulo'=>'Projeto Classe']); } $confirmar=$usuario[0]->senha; $validaSenha=password_verify($senha,$confirmar); if(!$validaSenha) { return view('loginView', ['mensagem'=>'Senha inválida!','titulo'=>'Projeto Classe']); } $_SESSION['id']=$usuario[0]->id; $_SESSION['nome']=$usuario[0]->nome; return view('menuView'); } } arquivo loginView.php <div style="height:150px"></div> <!--empurrar o form para o meio da tela--> <main> <?php if($mensagem): ?> <div class="alert alert-success"><?=$mensagem?></div> <?php endif; $action=($titulo=="Alterar senha") ? "?Login.senhaAlterada" : "?Login.verificar"; ?> <form method=post action=<?=$action?>> <h1 class="h3 mb-3 fw-normal"><?=$titulo?></h1> <div class="form-floating"> <input type="email" class="form-control" name=email autofocus> <label>Email</label> </div> <div class="my-1"></div> <div class="form-floating"> <?php $chave=($titulo=="Alterar senha") ? "senhaAlterada" : "senha"; ?> <input type="password" class="form-control" name=<?=$chave?> required ondblclick="this.type='text'"> <label>Duplo clique para ver a Senha</label> <!--Google Chrome não mostra o olho--> </div> <div class="my-3"></div> <button class="w-100 btn btn-lg btn-primary" type="submit">Entrar</button> </form> </main> arquivo menuView.php <header class="navbar navbar-light sticky-top bg-light flex-md-nowrap p-0"> Classe <div class=dropdown> <button type=button class="btn btn-outline-primary dropdown-toggle" data-bs-toggle=dropdown id=btmenu> Menu </button> <ul class="dropdown-menu"> <?php if($_SESSION['id']==1): ?> <li><a class=dropdown-item href=?Diario.inicio>Diário</a></li> <li><a class=dropdown-item href=?Balancete.inicio>Balancete</a></li> <li><a class=dropdown-item href=?Pagar.inicio>Pagar</a></li> <li><a class=dropdown-item href=?Outros.inicio>Outros</a></li> <?php endif; ?> <li><a class=dropdown-item href=?Orcamento.inicio>Orçamento</a></li> <li><a class=dropdown-item href=?Pessoa.inicio>Pessoa</a></li> <li><a class=dropdown-item href=?Produto.inicio>Produto</a></li> <li><a class=dropdown-item href=?Venda.inicio>Relatório de Venda</a></li> <li><a class=dropdown-item href=?MercadoPago.inicio>Mercado Pago</a></li> </ul> </div> <div class="dropdown"> <button class="btn btn-outline-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false"> <?=$_SESSION['nome']?> </button> <ul class="dropdown-menu"> <li><a class=dropdown-item href=?Login.sair>Sair</a></li> <li><a class=dropdown-item href=?Login.alterarSenha>Alterar senha</a></li> </ul> </div> </header>
  21. Frank K Hosaka

    Sr.

    Eu tenho um código que puxa todos os pedidos, mas peço para exibir apenas 20 de cada vez, assim: arquivo historico.php <?php include 'menu.php'; if(isset($_GET['ped'])) { $_SESSION['ped']=$_GET['ped']; return header("location:orcamento.php"); } $where=""; if(isset($_GET['hist'])) { $hist=$_GET['hist']; $ped=$_SESSION['referencia']+$hist; $where="where ped <= $ped"; } $query=$mysqli->query("select * from tbpedido left join tbpessoa on tbpedido.codp = tbpessoa.codp $where order by ped desc limit 20"); $pedidos=$query->fetch_all(MYSQLI_ASSOC); $_SESSION['referencia']=$pedidos[0]['ped']; ?> <script>btmenu.innerHTML='Histórico Orçamentos';document.title='Histórico Orçamento'</script> <main> <table class='table table-striped linha'> <th style=width:20px>Dia <th style=width:40px>Pedido <th>Vendido<th>Bling <th class=text-end>Total<th>Cliente <?php foreach($pedidos as $ped) : ?> <tr><td><?=date('d/m/y',strtotime($ped['dia']))?> <td class=text-end><a href=?ped=<?=$ped['ped']?>><?=$ped['ped']?></a> <td> <?php if($ped['horavenda']): ?> <?=date('d/m H:i',strtotime($ped['horavenda']))?> <?php endif; ?> <td><?=$ped['bling']?> <td class=text-end><?=dec($ped['total'])?> <td><?=$ped['pessoa']?> <?php endforeach; ?> </table> <div style="display:flex;width:30px;margin-right:15px"> <a href="?hist=-19">Anterior</a>&nbsp; <a href="orcamento.php">Voltar</a>&nbsp; <a href="?hist=19" >Posterior</a> </div> </main>
  22. Trabalhar com classes no PHP não é nada fácil, eu apanhei muito para fazer os meus códigos funcionarem, eu fui obrigado a criar três pastas: Modelos, Visoes e Controles. Mas nesse mês, o Iowys apresentou um novo autoload que permite ao programador trabalhar com as classes sem a necessidade de criar pastas, mas a versão do PHP tem que ser a 8.2 para cima. O meu primeiro projeto foi criado na base da tentativa e erro, ele recebeu o nome de orcamento.php, mas eu tive que fazer várias adaptações, quando o meu irmão começou a utilizar. Em seguida, apareceram divergências entre o relatório da vendas do meu código com o da Bling (o meu programa não emite nota fiscal, já a Bling emite a Nota Fiscal). A Bling tem uma rotina para montar o pedido de venda, mas ele acha bem mais fácil usar o meu código num antigo tablet da Apple, e depois ele exporta o meu pedido para a Bling. Para tentar eliminar a divergência, eu criei dois arquivos: orcamentoGet.php e OrcamentoVenda.php. Graças a esses dois arquivos, eu consigo fazer os relatórios de vendas serem iguais. Com o novo autoload eu posso melhorar os códigos assim: orcamentoControle.php orcamentoView.php A ideia básica das classes é separar o formulário HTML do código PHP e definir dentro do código quem vai fazer o quê. Falar é fácil, mas o novo autoload já ajuda bastante. arquivo index.php <?php spl_autoload_register(fn ($class) => require str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php'); (new Teste)->inicio(); arquivo Teste.php <?php class Teste { public function inicio() { echo "olá mundo"; } }
  23. O meu projeto de orçamento é basicamente isso: <?php date_default_timezone_set('America/Sao_Paulo'); $mysqli=new mysqli("localhost","root","","teste"); if(isset($_GET['vendido'])) { $horaVenda=date('Y-m-d H:i:s'); $mysqli->query("update tbpedido set horavenda='$horaVenda' where ped=1"); $horaVendida=$mysqli->query("select horavenda from tbpedido where ped=1") ->fetch_assoc()['horavenda']; echo $horaVendida . "<br>"; } ?> <form> <input type=submit value=Vendido> <input type=hidden name=vendido value='vendido'> </form> É um programa simples, mas encontrei divergência com o relatório da Bling. O último pedido que foi vendido no dia 20 aparece como dia 21. Olhando o código, tudo me leva a crer que alguém atualizou a tela do navegador no dia seguinte. Eu criei uma rotina para ver se alguém apertou o botão vendido. Isso acontece sempre mas não o último pedido. Desconfio que o problema é o navegador, de algum modo ele traz todas as informações do dia anterior e não sei como ele consegue executar esse código. Hoje, dividi o programa em dois arquivos. Um para apertar o botão e o outro para atualizar o banco de dados. Ou seja, não há mais como o navegador executar o código sem a ajuda do usuário, penso eu. O código final ficou assim: arquivo index.php <form action=registraVenda.php> <input type=submit value=Vendido> <input type=hidden name=vendido value='vendido'> </form> arquivo registraVenda.php <?php date_default_timezone_set('America/Sao_Paulo'); $mysqli=new mysqli("localhost","root","","teste"); $horaVenda=date('Y-m-d H:i:s'); $mysqli->query("update tbpedido set horavenda='$horaVenda' where ped=1"); $horaVendida=$mysqli->query("select horavenda from tbpedido where ped=1") ->fetch_assoc()['horavenda']; echo $horaVendida . "<br>";
  24. Faz três meses que estou procurando um erro eventual no meu projeto orçamento, de noite o relatório de vendas do PHP e do Bling batem, mas no dia seguinte não bate, não é sempre que isso acontece, daí dei o nome de erro eventual. Eu sei que sou um péssimo programador, mas hoje eu levei um baita susto. Alterei o programa orçamento assim: <?php exit; Ou seja, tudo o que ele tinha que fazer é não fazer absolutamente nada. Mas ele executou toda a rotina do orçamento, o PHP simplesmente não considerou as minhas alterações. Eu fiquei desesperado. Estou há três meses procurando o erro, e agora o PHP não aceita as minhas últimas alterações. Fiz o teste São Tomé, mudei o nome do arquivo do orcamento.php para qualquercoisa.php, e na hora de chamar o orçamento, o PHP reclamou que o arquivo não existia. Fui lá no Hostinger, fiz alteração no orçamento, e lá tudo funcionou como eu esperava. O meu notebook tem cinco anos, eu acho que não é tão velho assim. A minha última tentativa para o PHP aceitar as alterações no arquivo foi reiniciar o Windows. Graças a Deus deu certo, agora o PHP executa as minhas alterações.
  25. <?php session_start(); if(!isset($_SESSION['var'])) { $_SESSION['var']=0; } if(isset($_GET['test'])) { $_SESSION['var']++; } ?> <form> <input type=submit name=test value=<?=$_SESSION['var']?> /> </form>
×
×
  • Criar Novo...